ContactsProvider2.java revision 5df7e46835c4f103b05407660b4769edd515760f
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;
654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
66ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
67ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
6809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
6909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
7108ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwoodimport android.database.sqlite.SQLiteDoneException;
724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
74d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
7551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikovimport android.os.AsyncTask;
766ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
77ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
78b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
790dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikovimport android.os.SystemClock;
800e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
82508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
833de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
84b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
8597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
8697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
8797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
8897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
8997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
9097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
9197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
9297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
9397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
9497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
95ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
963de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
975b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
983de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
99d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
1005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.FullNameStyle;
1013de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
102bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1033de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
10409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1053de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
106916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1073de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
10882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
10997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
11097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
11197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
112a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
113a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
114c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
116d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
117b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
118d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
119d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
12042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
12342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
124b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1250e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
127622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
128b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1290e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
130ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1365b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
137caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
138bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
139bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
140bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
142619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
143619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
144619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1483d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
149b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1503d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1513d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
153b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
154b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
15551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
1563d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1570dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
1580dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
1590dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
1600e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1610e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
162a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
164dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov    private static final String TIMES_CONTACTED_SORT_COLUMN = "times_contacted_sort";
1655e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
166d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
167dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov            + TIMES_CONTACTED_SORT_COLUMN + " DESC, "
1689b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
169d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
170d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
171d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
172d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1736e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
1749b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
1759b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1769b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
1779b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
1786e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
1799b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
1809b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1819b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
1829b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
183de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
184de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
185d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
186d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
189a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
1905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
194a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
195f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
19642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private static final int CONTACTS_AS_MULTI_VCARD = 1011;
1972149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_DATA = 1012;
1982149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_DATA = 1013;
199a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_ENTITIES = 1014;
200a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ENTITIES = 1015;
201a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1016;
2024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
20646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
2074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
210ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
21148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
21248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
21348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
21448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
21548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
21648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
21748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
21848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
219a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
222b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
223b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
224b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
22582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
22682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
22831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
22931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
230eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
231eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
232ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
233ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
234ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
235ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
23635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
237b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
23835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
239c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
240c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
241c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
24746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
24846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
24909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
25009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
251d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
252d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
253d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
2547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
2557a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
256dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
257dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
258dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
259dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
260dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
261dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE
262dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
263dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
264dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
265dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
266dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
267dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
268dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
269dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND "
270dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + Groups.AUTO_ADD + " != 0";
271dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
272dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
273dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
274dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
275dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
276dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
277dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
278dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
279dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
280dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
281dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
282dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
283d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
284f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
285f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
286f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
28767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
28867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2896cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
291f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
292ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
293ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
294d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
29567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
296d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
297ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
29914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
30067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
3013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
30288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
3033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
3043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
3055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
3063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
307f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
30888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
30988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
31088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
31188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
31288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
31388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
31488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
315f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
3163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
3173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
31814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
3193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
3205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
3213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
322f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
3233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
3243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
32514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
326321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
32720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
32820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
329321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
330321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
33120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
33220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
333f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
33419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private interface RawContactsQuery {
33519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
33619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
33719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
338ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
339ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
340ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
34119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
34219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
34319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
344ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
345ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
34619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
34719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
348c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
349df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
350caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
35171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
35271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
35371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
35471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
35571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
35671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
35771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
35871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
35971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
36071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
36171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
36271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
36371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
36471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
365a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
366a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
367a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
368a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
369a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
370a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
371a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
372a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
373a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
374a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
375a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
376a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
377c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Current contacts - those contacted within the last 3 days (in seconds)
378c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_CURRENT = 3 * 24 * 60 * 60;
379c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
380c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    // Recent contacts - those contacted within the last 30 days (in seconds)
381c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
382c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
383c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String TIME_SINCE_LAST_CONTACTED =
384c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            "(strftime('%s', 'now') - " + Contacts.LAST_TIME_CONTACTED + "/1000)";
385c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
386c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    /*
387c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Sorting order for email address suggestions: first starred, then the rest.
388c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * Within the starred/unstarred groups - three buckets: very recently contacted, then fairly
389c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * recently contacted, then the rest.  Within each of the bucket - descending count
390c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * of times contacted. If all else fails, alphabetical.  (Super)primary email
391c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     * address is returned before other addresses for the same contact.
392c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov     */
393c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov    private static final String EMAIL_FILTER_SORT_ORDER =
394c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            "(CASE WHEN " + Contacts.STARRED + "=1 THEN 0 ELSE 1 END), "
395c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + "(CASE WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_CURRENT + " THEN 0 "
396c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + " WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_RECENT + " THEN 1 "
397c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + " ELSE 2 END),"
398c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Contacts.TIMES_CONTACTED + " DESC, "
399c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Contacts.DISPLAY_NAME + ", "
400c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Data.CONTACT_ID + ", "
401c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov            + Data.IS_SUPER_PRIMARY + " DESC";
402c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov
403916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
404916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
405916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
406916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
407916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NICKNAME + "," +
408916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_SHORTHAND + "," +
409f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.ORGANIZATION + "," +
410f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.NAME_CONSONANTS;
411916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
412f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    /**
413f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * If any of these columns are used in a Data projection, there is no point in
414f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     * using the DISTINCT keyword, which can negatively affect performance.
415f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov     */
416f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    private static final String[] DISTINCT_DATA_PROHIBITING_COLUMNS = {
417f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data._ID,
418f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.RAW_CONTACT_ID,
419f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            Data.NAME_RAW_CONTACT_ID,
420f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
421f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
422f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.DIRTY,
423f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.NAME_VERIFIED,
424f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.SOURCE_ID,
425f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov            RawContacts.VERSION,
426f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov    };
427916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
428f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
429f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
430f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
431f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
432f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
433f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
434f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
435f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
436f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
437f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
438f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
4393d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_URI)
4403d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(Contacts.PHOTO_THUMBNAIL_URI)
441f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
442f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
443f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
444f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
445f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
446cf832869bcf91b8037d8b7f510a3a213b30764a3Dmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
447f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
448f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
449f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
450f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
451f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
452f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
453f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
454f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
455f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
456f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
457f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
458f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
459f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
460f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
461f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
462f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
463f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
464f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
465f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
466f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
467f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_MIMETYPE)
468f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA_ID)
469f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA1)
470f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA2)
471f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA3)
472f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA4)
473f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
474f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
475f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
476f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
477f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
478f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
479f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
480f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
481f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
482f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
483f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
484f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
485f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
486f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
487f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
488f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
489f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
490f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
491f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
492f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
493f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
494f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
495f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
496f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
497f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
498f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
499f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
500f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
501f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
502f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
503f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
504f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
505f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
506f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
507f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
508f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
509f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
510f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
511f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
512f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
513f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
514f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
515f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
516f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
517f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
518f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
519f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
520f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
521f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
522f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
523f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
524f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
525f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
532f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
533f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
547038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
552e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
561916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
566916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5675e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, Contacts.TIMES_CONTACTED)
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
578f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
584ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
606a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.IS_RESTRICTED)
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
619a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.IS_RESTRICTED)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
6693d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_URI, "contacts_view." + Contacts.PHOTO_URI)
6703d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov            .add(PhoneLookup.PHOTO_THUMBNAIL_URI, "contacts_view." + Contacts.PHOTO_THUMBNAIL_URI)
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
6772530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
680ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
698c039cfb78c40730483fd71178df63ada5826a315Dmitri Plotnikov            .add(Groups.GROUP_IS_READ_ONLY)
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
701f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
704f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
705ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
709f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
710f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
712f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ")")
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
716f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
717f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Contacts.HAS_PHONE_NUMBER + ")")
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
722373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
730eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
762f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
763f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
764f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
765f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
766f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
767f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
76882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
770f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
771f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
772f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
773f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
774f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
775f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
776f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
777f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
778f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
779f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
780f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
781f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
782f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
783f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
784f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
785f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
786f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
787f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
788f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
789f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7901b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
791f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
792f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
793f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
794f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
795f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
796f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
797f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
798f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
799d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
800f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
801f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
802f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
803f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
804f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
805f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
806f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
807f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
808f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
809778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.SHORTCUT_SUPPORT)
810778d92d4dce5f76c649e2aca9d00d3f214cd7643Dmitri Plotnikov            .add(Directory.PHOTO_SUPPORT)
811f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
8127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
8139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
8149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
8159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
8169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
8179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
8189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
8192526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
8202526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
821bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
822bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
823bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
824bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
82551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
826f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
8271129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
8281129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
8292526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
8302526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
831f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
832f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
8334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
8344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
835a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
836d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
837d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
838a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
839a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
8403653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
8413653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
8422d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
8432d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
844a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
845c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
8465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
8475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
8482149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
8495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
8502149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
8512149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
852a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
853a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
854a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
855a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
856f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
85742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
85842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
8595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
860ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
861ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
8625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
8633653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
8645ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
8655ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
8665ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
86746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
86846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
86946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
870b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
8714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
8724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
873ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
87448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
8755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
876ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
8774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
87848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
8791dac83b8fa58944acfd00f44e717a7dddc659d2dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
8805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
8815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
8824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
883ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
88448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
8851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
886ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
887ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
888ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
889ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
89035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
891b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
892b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
89335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
894a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
895b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
896b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
897b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
898b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
8994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
900eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
901eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
90282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
90382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
9041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
905c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
906c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
907c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
908c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
9092d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
910c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
911c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
9121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
9131b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
9141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
9151b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
9161b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
9171b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
9181b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
9191b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
92009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
92109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
922d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
923d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
924d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
9257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
9267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
92719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
92819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
929d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
930d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
931d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
932d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
933d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
934d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
935d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
936d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
937d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
9384458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
9394458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
940d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
9413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
9423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
9433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
944e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private static abstract class DataRowHandler {
945e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        protected final ContactsDatabaseHelper mDbHelper;
946e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        protected final ContactAggregator mContactAggregator;
947e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        protected String[] mSelectionArgs1 = new String[1];
9483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
949653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
9503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9511129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        @SuppressWarnings("all")
952e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public DataRowHandler(ContactsDatabaseHelper dbHelper, ContactAggregator aggregator,
953e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String mimetype) {
954e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            mDbHelper = dbHelper;
955e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            mContactAggregator = aggregator;
9563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
957a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
958a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
959a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
960a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
961a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
962a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
963a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
964a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
9653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
967653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
968653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
969b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
970653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
971653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
972653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
973653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
9743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
9753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
9763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
977d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
978d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values) {
979e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
980e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
9816dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            final Integer primary = values.getAsInteger(Data.IS_PRIMARY);
9826dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            final Integer superPrimary = values.getAsInteger(Data.IS_SUPER_PRIMARY);
9836dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            if ((primary != null && primary != 0) || (superPrimary != null && superPrimary != 0)) {
9846dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                final long mimeTypeId = getMimeTypeId();
98578fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.setIsPrimary(rawContactId, dataId, mimeTypeId);
9866dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann
9876dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                // We also have to make sure that no other data item on this raw_contact is
9886dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                // configured super primary
9896dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                if (superPrimary != null) {
9906dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    if (superPrimary != 0) {
99178fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        mDbHelper.setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
9926dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    } else {
99378fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        mDbHelper.clearSuperPrimary(rawContactId, mimeTypeId);
9946dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    }
9956dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                } else {
9966dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    // if there is already another data item configured as super-primary,
9976dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    // take over the flag (which will automatically remove it from the other item)
998189273b8a9ae75d88690febfbed2d635138799ecDmitri Plotnikov                    if (mDbHelper.rawContactHasSuperPrimary(rawContactId, mimeTypeId)) {
99978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        mDbHelper.setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
10006dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    }
10016dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                }
1002e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1003e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1004e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
10053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
10083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
10093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
1010813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         *
1011813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * @return true if update changed something
10123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1013d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public boolean update(SQLiteDatabase db, TransactionContext txContext,
1014d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
101514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
101614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1017653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
10186dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            handlePrimaryAndSuperPrimary(values, dataId, rawContactId);
1019653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1020653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
10214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
1022189273b8a9ae75d88690febfbed2d635138799ecDmitri Plotnikov                db.update(Tables.DATA, values, Data._ID + " =?", mSelectionArgs1);
1023653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1024653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1025f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
1026d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                txContext.markRawContactDirty(rawContactId);
1027653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1028813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1029813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
10303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10326dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann        /**
10336dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann         * Ensures that all super-primary and primary flags of this raw_contact are
10346dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann         * configured correctly
10356dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann         */
10366dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann        private void handlePrimaryAndSuperPrimary(ContentValues values, long dataId,
10376dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                long rawContactId) {
10386dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            final boolean hasPrimary = values.containsKey(Data.IS_PRIMARY);
10396dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            final boolean hasSuperPrimary = values.containsKey(Data.IS_SUPER_PRIMARY);
10406dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann
10416dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            // Nothing to do? Bail out early
10426dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            if (!hasPrimary && !hasSuperPrimary) return;
10436dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann
10446dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            final long mimeTypeId = getMimeTypeId();
10456dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann
10466dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            // Check if we want to clear values
10476dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            final boolean clearPrimary = hasPrimary &&
10486dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    values.getAsInteger(Data.IS_PRIMARY) == 0;
10496dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            final boolean clearSuperPrimary = hasSuperPrimary &&
10506dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    values.getAsInteger(Data.IS_SUPER_PRIMARY) == 0;
10516dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann
10526dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            if (clearPrimary || clearSuperPrimary) {
10536dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                // Test whether these values are currently set
1054189273b8a9ae75d88690febfbed2d635138799ecDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
10556dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                final String[] cols = new String[] { Data.IS_PRIMARY, Data.IS_SUPER_PRIMARY };
1056189273b8a9ae75d88690febfbed2d635138799ecDmitri Plotnikov                final Cursor c = mDbHelper.getReadableDatabase().query(Tables.DATA,
1057189273b8a9ae75d88690febfbed2d635138799ecDmitri Plotnikov                        cols, Data._ID + "=?", mSelectionArgs1, null, null, null);
10586dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                try {
10596dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    if (c.moveToFirst()) {
10606dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                        final boolean isPrimary = c.getInt(0) != 0;
10616dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                        final boolean isSuperPrimary = c.getInt(1) != 0;
10626dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                        // Clear values if they are currently set
10636dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                        if (isSuperPrimary) {
106478fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                            mDbHelper.clearSuperPrimary(rawContactId, mimeTypeId);
10656dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                        }
10666dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                        if (clearPrimary && isPrimary) {
106778fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                            mDbHelper.setIsPrimary(rawContactId, -1, mimeTypeId);
10686dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                        }
10696dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    }
10706dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                } finally {
10716dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    c.close();
10726dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                }
10736dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            } else {
10746dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                // Check if we want to set values
10756dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                final boolean setPrimary = hasPrimary &&
10766dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                        values.getAsInteger(Data.IS_PRIMARY) != 0;
10776dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                final boolean setSuperPrimary = hasSuperPrimary &&
10786dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                        values.getAsInteger(Data.IS_SUPER_PRIMARY) != 0;
10796dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                if (setSuperPrimary) {
10806dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    // Set both super primary and primary
108178fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
108278fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.setIsPrimary(rawContactId, dataId, mimeTypeId);
10836dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                } else if (setPrimary) {
108478fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    // Primary was explicitly set, but super-primary was not.
10856dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    // In this case we set super-primary on this data item, if
10866dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    // any data item of the same raw-contact already is super-primary
1087189273b8a9ae75d88690febfbed2d635138799ecDmitri Plotnikov                    if (mDbHelper.rawContactHasSuperPrimary(rawContactId, mimeTypeId)) {
108878fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        mDbHelper.setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
10896dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                    }
109078fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.setIsPrimary(rawContactId, dataId, mimeTypeId);
10916dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann                }
10926dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            }
10936dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann
10946dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            // Now that we've taken care of clearing this, remove it from "values".
10956dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            values.remove(Data.IS_SUPER_PRIMARY);
10966dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann            values.remove(Data.IS_PRIMARY);
10976dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann        }
10986dd371aea88e09cbe56b8c483021f3bf61527331Daniel Lehmann
1099d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
110014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
110114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
110214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
11034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
11044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=?", mSelectionArgs1);
11054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
11064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
11073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
11085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
11093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
11103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
11113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11135ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
11144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            long mimeTypeId = getMimeTypeId();
1115e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
1116e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
11174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
11184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            Cursor c = db.query(DataDeleteQuery.TABLE,
11194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    DataDeleteQuery.CONCRETE_COLUMNS,
11204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=?" +
11214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        " AND " + DataColumns.MIMETYPE_ID + "=" + mimeTypeId,
11224da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
11233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
1124e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
112514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
1126f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
1127e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
1128e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
1129e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
1130e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
11313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
11323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
11333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
11343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
11354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            if (primaryId != -1) {
113678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.setIsPrimary(rawContactId, primaryId, mimeTypeId);
11374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            }
1138e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1139e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1140e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
1141e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
1142e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
1143e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
1144e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1145e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
11463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1148d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, TransactionContext txContext,
1149d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                long rawContactId) {
1150d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            if (!isNewRawContact(txContext, rawContactId)) {
11517e2635fa663312adb2bc9d04f50a6bb54c6cc5f4Dmitri Plotnikov                mContactAggregator.updateRawContactDisplayName(db, rawContactId);
1152fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(db, rawContactId);
1153285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
11543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1155a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1156d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        private boolean isNewRawContact(TransactionContext txContext, long rawContactId) {
1157d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return txContext.isNewRawContact(rawContactId);
1158d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        }
1159d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov
1160622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1161622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
1162813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * as baseline, but augmented with any updates.  Returns null if there is
1163813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * no change.
1164622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1165622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
1166622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
1167813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            boolean changing = false;
1168622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
11694da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
11704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=?",
11714da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
1172622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
1173622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
1174622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
1175622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
1176813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        final String value = cursor.getString(i);
1177813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        if (!changing && update.containsKey(key)) {
1178813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            Object newValue = update.get(key);
1179813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            String newString = newValue == null ? null : newValue.toString();
1180813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            changing |= !TextUtils.equals(newString, value);
1181813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        }
1182813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        values.put(key, value);
1183622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
1184622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
1185622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
1186622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1187622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1188813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!changing) {
1189813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return null;
1190813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1191813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1192622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1193622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1194622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1195e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
1196e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public void triggerAggregation(long rawContactId) {
1197e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            mContactAggregator.triggerAggregation(rawContactId);
1198e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        }
11993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1201e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class CustomDataRowHandler extends DataRowHandler {
12023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1203e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public CustomDataRowHandler(ContactsDatabaseHelper dbHelper, ContactAggregator aggregator,
1204e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String mimetype) {
1205e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            super(dbHelper, aggregator, mimetype);
12063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12095df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    public static class StructuredNameRowHandler extends DataRowHandler {
1210622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
12115df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        private final NameLookupBuilder mNameLookupBuilder;
12123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1213e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public StructuredNameRowHandler(ContactsDatabaseHelper dbHelper,
12145df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                ContactAggregator aggregator, NameSplitter splitter,
12155df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                NameLookupBuilder nameLookupBuilder) {
1216e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            super(dbHelper, aggregator, StructuredName.CONTENT_ITEM_TYPE);
1217622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
12185df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov            mNameLookupBuilder = nameLookupBuilder;
12193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
1222d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
1223d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values) {
1224622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
122514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1226d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            long dataId = super.insert(db, txContext, rawContactId, values);
122714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1228f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1229d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            Integer fullNameStyle = values.getAsInteger(StructuredName.FULL_NAME_STYLE);
12305df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov            mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name,
123151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                    fullNameStyle != null
12325df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                            ? mSplitter.getAdjustedFullNameStyle(fullNameStyle)
123351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            : FullNameStyle.UNDEFINED);
123448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId, values);
1235d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            fixRawContactDisplayName(db, txContext, rawContactId);
1236813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
123714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
123814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
123914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
124014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1241d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
1242d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                Cursor c, boolean callerIsSyncAdapter) {
1243622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1244622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1245cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1246622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1247813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {  // No change
1248813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1249813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1250813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1251622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
125214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1253d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            super.update(db, txContext, values, c, callerIsSyncAdapter);
12547ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME) ||
12557ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_FAMILY_NAME) ||
12567ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME) ||
12577ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)) {
12587ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                augmented.putAll(values);
12597ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                String name = augmented.getAsString(StructuredName.DISPLAY_NAME);
126078fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteNameLookup(dataId);
12617ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                Integer fullNameStyle = augmented.getAsInteger(StructuredName.FULL_NAME_STYLE);
12625df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name,
126351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                        fullNameStyle != null
12645df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                                ? mSplitter.getAdjustedFullNameStyle(fullNameStyle)
126551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                : FullNameStyle.UNDEFINED);
12667ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                insertNameLookupForPhoneticName(rawContactId, dataId, augmented);
126714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
1268d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            fixRawContactDisplayName(db, txContext, rawContactId);
1269813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
1270813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
127114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
127214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
127314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1274d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
127514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
127614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
127714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1278d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            int count = super.delete(db, txContext, c);
127914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
128078fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.deleteNameLookup(dataId);
1281d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            fixRawContactDisplayName(db, txContext, rawContactId);
1282813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
128314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
12843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1287622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
12883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1289622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1290622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1291622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1292622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
12933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1294622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1295622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1296622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1297622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1298622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1299622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
13007a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        public void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
130167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1302622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
130367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
130467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1305622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1306622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
13078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1308622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1309622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
131067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
131167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
131267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
131367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
131467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
131567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
13168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1317622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
13184cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                // As the name could be changed, let's guess the name style again.
13194cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                name.fullNameStyle = FullNameStyle.UNDEFINED;
13204cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                mSplitter.guessNameStyle(name);
1321ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                int unadjustedFullNameStyle = name.fullNameStyle;
1322ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                name.fullNameStyle = mSplitter.getAdjustedFullNameStyle(name.fullNameStyle);
13235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                final String joined = mSplitter.join(name, true);
1324622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
13255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
1326ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                update.put(StructuredName.FULL_NAME_STYLE, unadjustedFullNameStyle);
13275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
13284cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao            } else if (touchedUnstruct && touchedStruct){
1329d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.FULL_NAME_STYLE)) {
1330d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.FULL_NAME_STYLE,
1331d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessFullNameStyle(unstruct));
13324cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1333d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.PHONETIC_NAME_STYLE)) {
1334d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.PHONETIC_NAME_STYLE,
1335d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessPhoneticNameStyle(unstruct));
13364cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1337622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1338622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
13395df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
13405df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        public void insertNameLookupForPhoneticName(long rawContactId, long dataId,
13415df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                ContentValues values) {
13425df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov            if (values.containsKey(StructuredName.PHONETIC_FAMILY_NAME)
13435df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                    || values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)
13445df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                    || values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME)) {
13455df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                mDbHelper.insertNameLookupForPhoneticName(rawContactId, dataId,
13465df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                        values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
13475df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                        values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
13485df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                        values.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
13495df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov            }
13505df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        }
1351622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1352622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1353e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class StructuredPostalRowHandler extends DataRowHandler {
1354622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1355622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1356e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public StructuredPostalRowHandler(ContactsDatabaseHelper dbHelper,
1357e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                ContactAggregator aggregator, PostalSplitter splitter) {
1358e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            super(dbHelper, aggregator, StructuredPostal.CONTENT_ITEM_TYPE);
1359622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1360622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1361622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1362622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1363d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
1364d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values) {
1365622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1366d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return super.insert(db, txContext, rawContactId, values);
1367622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1368622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1369622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1370d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
1371d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                Cursor c, boolean callerIsSyncAdapter) {
1372622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1373622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1374813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {    // No change
1375813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1376813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1377813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1378622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1379d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            super.update(db, txContext, values, c, callerIsSyncAdapter);
1380813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1381622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1382622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1383622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1384622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1385622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1386622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1387622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1388622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1389622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1390622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1391622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1392622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1393622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1394622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1395622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1396622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1397622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1398622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
139967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
140067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
140167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
140267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1403622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1404622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1405622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1406622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1407622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1408622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
140967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
141067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
141167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1412622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1413622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1414622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
14153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
14163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
14183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1419e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class CommonDataRowHandler extends DataRowHandler {
14203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
14223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
14233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1424e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public CommonDataRowHandler(ContactsDatabaseHelper dbHelper, ContactAggregator aggregator,
1425e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String mimetype, String typeColumn, String labelColumn) {
1426e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            super(dbHelper, aggregator, mimetype);
14273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
14283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
14293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
1432d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
1433d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values) {
1434622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1435d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return super.insert(db, txContext, rawContactId, values);
1436622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
14373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1438622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1439d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
1440d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                Cursor c, boolean callerIsSyncAdapter) {
1441622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1442622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1443813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {        // No change
1444813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1445813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1446622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1447d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return super.update(db, txContext, values, c, callerIsSyncAdapter);
1448622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
14493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1450622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1451622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1452622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1453622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1454622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1455622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1456622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1457622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
14583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1459622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1460622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1461622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1462622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1463622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
14643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
14663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1467e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class OrganizationDataRowHandler extends CommonDataRowHandler {
14683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1469e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public OrganizationDataRowHandler(ContactsDatabaseHelper dbHelper,
1470e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                ContactAggregator aggregator) {
1471e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            super(dbHelper, aggregator,
1472e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
14733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
1476d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
1477d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values) {
1478a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1479a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1480a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1481d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            long dataId = super.insert(db, txContext, rawContactId, values);
1482a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1483d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            fixRawContactDisplayName(db, txContext, rawContactId);
1484e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            mDbHelper.insertNameLookupForOrganization(rawContactId, dataId, company, title);
1485a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
14863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
1489d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
1490d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                Cursor c, boolean callerIsSyncAdapter) {
1491d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
1492813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1493813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
149414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
149531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsCompany = values.containsKey(Organization.COMPANY);
149631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsTitle = values.containsKey(Organization.TITLE);
149731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            if (containsCompany || containsTitle) {
1498813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1499813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1500813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
150131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String company;
150231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
150331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsCompany) {
150431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = values.getAsString(Organization.COMPANY);
150531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
150631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
150731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = DatabaseUtils.stringForQuery(db,
150831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.COMPANY +
150931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
151031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
151131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
151231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
151331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String title;
151431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsTitle) {
151531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = values.getAsString(Organization.TITLE);
151631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
151731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
151831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = DatabaseUtils.stringForQuery(db,
151931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.TITLE +
152031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
152131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
152231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
152331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
152478fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteNameLookup(dataId);
1525e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                mDbHelper.insertNameLookupForOrganization(rawContactId, dataId, company, title);
152631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
1527d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                fixRawContactDisplayName(db, txContext, rawContactId);
152831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            }
1529813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
153014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
153114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
153214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1533d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
1534a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
153514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
153614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1537d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            int count = super.delete(db, txContext, c);
1538d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            fixRawContactDisplayName(db, txContext, rawContactId);
153978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.deleteNameLookup(dataId);
154014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
154114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
154214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
154314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
15443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
15453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
15463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
15473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
15483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
15493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
15503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
15513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
15523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
15533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1554e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class EmailDataRowHandler extends CommonDataRowHandler {
1555e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1556e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public EmailDataRowHandler(ContactsDatabaseHelper dbHelper, ContactAggregator aggregator) {
1557e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            super(dbHelper, aggregator, Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1558e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1559e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1560e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1561d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
1562d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values) {
1563813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String email = values.getAsString(Email.DATA);
156414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1565d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            long dataId = super.insert(db, txContext, rawContactId, values);
156614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1567d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            fixRawContactDisplayName(db, txContext, rawContactId);
1568e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            String address = mDbHelper.insertNameLookupForEmail(rawContactId, dataId, email);
1569813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (address != null) {
1570813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1571813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
157214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
157314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
157414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
157514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1576d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
1577d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                Cursor c, boolean callerIsSyncAdapter) {
1578d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
1579813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1580813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
158114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1582b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Email.DATA)) {
1583813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1584813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1585813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1586b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String address = values.getAsString(Email.DATA);
158778fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteNameLookup(dataId);
1588e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                mDbHelper.insertNameLookupForEmail(rawContactId, dataId, address);
1589d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                fixRawContactDisplayName(db, txContext, rawContactId);
1590813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1591b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1592813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1593813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
159414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
159514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
159614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1597d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
159814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
159914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
160014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1601d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            int count = super.delete(db, txContext, c);
160214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
160378fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.deleteNameLookup(dataId);
1604d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            fixRawContactDisplayName(db, txContext, rawContactId);
1605813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
160614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1607e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1608e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1609e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1610e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1611e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1612e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1613e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1614e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1615e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1616e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1617e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1618e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1619e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1620e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1621e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class NicknameDataRowHandler extends CommonDataRowHandler {
162214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1623e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public NicknameDataRowHandler(ContactsDatabaseHelper dbHelper,
1624e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                ContactAggregator aggregator) {
1625e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            super(dbHelper, aggregator, Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
162614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
162714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
162814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1629d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
1630d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values) {
163114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
163214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1633d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            long dataId = super.insert(db, txContext, rawContactId, values);
163414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1635813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!TextUtils.isEmpty(nickname)) {
1636d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                fixRawContactDisplayName(db, txContext, rawContactId);
1637e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                mDbHelper.insertNameLookupForNickname(rawContactId, dataId, nickname);
1638813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1639813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
164014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
164114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
164214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
164314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1644d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
1645d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                Cursor c, boolean callerIsSyncAdapter) {
164614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
164714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
164814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1649d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
1650813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1651813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
165214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1653b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Nickname.NAME)) {
1654b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String nickname = values.getAsString(Nickname.NAME);
165578fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteNameLookup(dataId);
1656e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                mDbHelper.insertNameLookupForNickname(rawContactId, dataId, nickname);
1657d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                fixRawContactDisplayName(db, txContext, rawContactId);
1658813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1659b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1660813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1661813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
166214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
166314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
166414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1665d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
166614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
166714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
166814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1669d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            int count = super.delete(db, txContext, c);
167014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
167178fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.deleteNameLookup(dataId);
1672d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            fixRawContactDisplayName(db, txContext, rawContactId);
1673813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
167414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
167514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
167614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
167714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1678e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class PhoneDataRowHandler extends CommonDataRowHandler {
16793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1680e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public PhoneDataRowHandler(ContactsDatabaseHelper dbHelper, ContactAggregator aggregator) {
1681e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            super(dbHelper, aggregator, Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
16823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
16833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
16843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
1685d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
1686d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values) {
16870b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
16880b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
16890b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
16906206cd7e15b1fe63b72cc9ba32a4d84c764963ceDmitri Plotnikov
1691d015a8321fb9dcbfa96becb909145dfcce3da608Bai Tao                String numberE164 =
1692e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        PhoneNumberUtils.formatNumberToE164(number, mDbHelper.getCurrentCountryIso());
1693892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (numberE164 != null) {
1694892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    values.put(PhoneColumns.NORMALIZED_NUMBER, numberE164);
1695892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
1696d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                dataId = super.insert(db, txContext, rawContactId, values);
1697653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1698892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, numberE164);
1699285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
1700d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                fixRawContactDisplayName(db, txContext, rawContactId);
1701892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (numberE164 != null) {
1702813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                    triggerAggregation(rawContactId);
1703813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                }
17040b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
1705d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                dataId = super.insert(db, txContext, rawContactId, values);
17060b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1707653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1708653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1709653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1710653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1711d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
1712d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                Cursor c, boolean callerIsSyncAdapter) {
1713813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String number = null;
1714892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            String numberE164 = null;
17150b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1716813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                number = values.getAsString(Phone.NUMBER);
1717892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (number != null) {
1718e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    numberE164 = PhoneNumberUtils.formatNumberToE164(number,
1719e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                            mDbHelper.getCurrentCountryIso());
1720892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
1721892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (numberE164 != null) {
1722892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    values.put(PhoneColumns.NORMALIZED_NUMBER, numberE164);
1723892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
1724813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1725653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1726d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
1727813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1728813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1729653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1730813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1731813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1732813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1733892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, numberE164);
1734285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
1735d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                fixRawContactDisplayName(db, txContext, rawContactId);
1736813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
17370b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1738813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
173914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
174014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
174114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1742d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
174314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
174414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
174514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1746d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            int count = super.delete(db, txContext, c);
174714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
174814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1749285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
1750d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            fixRawContactDisplayName(db, txContext, rawContactId);
1751813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
175214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1753653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1754653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1755653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1756892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String number, String numberE164) {
1757892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
1758892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=?", mSelectionArgs1);
1759e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1760892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber = PhoneNumberUtils.normalizeNumber(number);
1761892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (!TextUtils.isEmpty(normalizedNumber)) {
1762892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    ContentValues phoneValues = new ContentValues();
1763892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1764892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1765892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
1766892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.MIN_MATCH,
1767892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                            PhoneNumberUtils.toCallerIDMinMatch(normalizedNumber));
1768892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
1769892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov
1770892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (numberE164 != null && !numberE164.equals(normalizedNumber)) {
1771892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, numberE164);
1772892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        phoneValues.put(PhoneLookupColumns.MIN_MATCH,
1773892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                PhoneNumberUtils.toCallerIDMinMatch(numberE164));
1774892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
1775892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
1776892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
1777e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
17783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
17793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
17803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
17813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
17823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
17833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
17843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
17853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
17863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
17873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
17883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
17893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
17903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
17913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
17923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
17933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
17943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
17953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1796e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupMembershipRowHandler extends DataRowHandler {
1797653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1798dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private static final String SELECTION_RAW_CONTACT_ID = RawContacts._ID + "=?";
1799dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1800dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private static final String QUERY_COUNT_FAVORITES_GROUP_MEMBERSHIPS_BY_RAW_CONTACT_ID =
1801dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                "SELECT COUNT(*) FROM " + Tables.DATA + " LEFT OUTER JOIN " + Tables .GROUPS
1802dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " ON " + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID
1803dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + "=" + GroupsColumns.CONCRETE_ID
1804dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
1805dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " AND " + Tables.DATA + "." + GroupMembership.RAW_CONTACT_ID + "=?"
1806dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " AND " + Groups.FAVORITES + "!=0";
1807dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1808e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        private final HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache;
1809e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
1810e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public GroupMembershipRowHandler(ContactsDatabaseHelper dbHelper,
1811e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                ContactAggregator aggregator,
1812e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                HashMap<String, ArrayList<GroupIdCacheEntry>> groupIdCache) {
1813e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            super(dbHelper, aggregator, GroupMembership.CONTENT_ITEM_TYPE);
1814e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            mGroupIdCache = groupIdCache;
1815653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1816653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1817653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1818d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
1819d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values) {
1820e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            resolveGroupSourceIdInValues(txContext, rawContactId, db, values, true);
1821d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            long dataId = super.insert(db, txContext, rawContactId, values);
1822dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (hasFavoritesGroupMembership(db, rawContactId)) {
1823dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, true /* starred */);
1824dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
18250be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
18260be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1827653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1828653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1829653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1830d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
1831d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                Cursor c, boolean callerIsSyncAdapter) {
183214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1833dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
1834e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            resolveGroupSourceIdInValues(txContext, rawContactId, db, values, false);
1835d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
1836813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1837813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1838dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
1839dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (wasStarred != isStarred) {
1840dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, isStarred);
1841dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
18420be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
1843813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
18440be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
18450be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
1846dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private void updateRawContactsStar(SQLiteDatabase db, long rawContactId, boolean starred) {
1847dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            ContentValues rawContactValues = new ContentValues();
1848dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            rawContactValues.put(RawContacts.STARRED, starred ? 1 : 0);
1849dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (db.update(Tables.RAW_CONTACTS, rawContactValues, SELECTION_RAW_CONTACT_ID,
1850dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{Long.toString(rawContactId)}) > 0) {
1851dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mContactAggregator.updateStarred(rawContactId);
1852dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1853dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1854dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1855dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private boolean hasFavoritesGroupMembership(SQLiteDatabase db, long rawContactId) {
1856dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final long groupMembershipMimetypeId = mDbHelper
1857dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
1858dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = 0 < DatabaseUtils
1859dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    .longForQuery(db, QUERY_COUNT_FAVORITES_GROUP_MEMBERSHIPS_BY_RAW_CONTACT_ID,
1860dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{Long.toString(groupMembershipMimetypeId), Long.toString(rawContactId)});
1861dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return isStarred;
1862dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1863dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
18640be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
1865d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
18660be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1867dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
1868d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            int count = super.delete(db, txContext, c);
1869dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
1870dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (wasStarred && !isStarred) {
1871dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, false /* starred */);
1872dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
18730be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
18740be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
18750be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
18760be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
18770be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
1878b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
18790be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
1880b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
18810be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1882653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1883653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1884e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        private void resolveGroupSourceIdInValues(TransactionContext txContext,
1885e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                long rawContactId, SQLiteDatabase db, ContentValues values, boolean isInsert) {
1886653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1887653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1888653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1889653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1890653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1891653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1892653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1893653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1894653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1895653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1896653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1897653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1898653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1899653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1900653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1901653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1902653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1903653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1904653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1905653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1906ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
1907e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        txContext.getAccountForRawContact(rawContactId));
1908653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1909653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1910653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1911653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1912e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
1913e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        /**
1914e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov         * Returns the group id of the group with sourceId and the same account as rawContactId.
1915e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov         * If the group doesn't already exist then it is first created,
1916e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov         * @param db SQLiteDatabase to use for this operation
1917e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov         * @param rawContactId the contact this group is associated with
1918e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov         * @param sourceId the sourceIf of the group to query or create
1919e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov         * @return the group id of the existing or created group
1920e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov         * @throws IllegalArgumentException if the contact is not associated with an account
1921e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov         * @throws IllegalStateException if a group needs to be created but the creation failed
1922e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov         */
1923e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
1924e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                Account account) {
1925e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
1926e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            if (account == null) {
1927e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(rawContactId);
1928e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
1929e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
1930e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                try {
1931e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    if (c.moveToFirst()) {
1932e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
1933e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
1934e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
1935e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                            account = new Account(accountName, accountType);
1936e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        }
1937e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    }
1938e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                } finally {
1939e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    c.close();
1940e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                }
1941e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            }
1942e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
1943e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            if (account == null) {
1944e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                throw new IllegalArgumentException("if the groupmembership only "
1945e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        + "has a sourceid the the contact must be associated with "
1946e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        + "an account");
1947e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            }
1948e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
1949e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
1950e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            if (entries == null) {
1951e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                entries = new ArrayList<GroupIdCacheEntry>(1);
1952e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                mGroupIdCache.put(sourceId, entries);
1953e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            }
1954e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
1955e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            int count = entries.size();
1956e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            for (int i = 0; i < count; i++) {
1957e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                GroupIdCacheEntry entry = entries.get(i);
1958e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) {
1959e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    return entry.groupId;
1960e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                }
1961e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            }
1962e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
1963e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            GroupIdCacheEntry entry = new GroupIdCacheEntry();
1964e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            entry.accountName = account.name;
1965e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            entry.accountType = account.type;
1966e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            entry.sourceId = sourceId;
1967e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            entries.add(0, entry);
1968e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
1969e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            // look up the group that contains this sourceId and has the same account name and type
1970e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            // as the contact refered to by rawContactId
1971e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
1972e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
1973e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    new String[]{sourceId, account.name, account.type}, null, null, null);
1974e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            try {
1975e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                if (c.moveToFirst()) {
1976e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    entry.groupId = c.getLong(0);
1977e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                } else {
1978e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    ContentValues groupValues = new ContentValues();
1979e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    groupValues.put(Groups.ACCOUNT_NAME, account.name);
1980e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    groupValues.put(Groups.ACCOUNT_TYPE, account.type);
1981e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    groupValues.put(Groups.SOURCE_ID, sourceId);
1982e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
1983e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    if (groupId < 0) {
1984e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        throw new IllegalStateException("unable to create a new group with "
1985e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                                + "this sourceid: " + groupValues);
1986e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    }
1987e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                    entry.groupId = groupId;
1988e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                }
1989e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            } finally {
1990e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                c.close();
1991e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            }
1992e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
1993e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            return entry.groupId;
1994e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        }
1995653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1996653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1997e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class PhotoDataRowHandler extends DataRowHandler {
1998a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1999e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov        public PhotoDataRowHandler(ContactsDatabaseHelper dbHelper, ContactAggregator aggregator) {
2000e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            super(dbHelper, aggregator, Photo.CONTENT_ITEM_TYPE);
2001a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2002a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
2003a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
2004d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
2005d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                ContentValues values) {
2006d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            long dataId = super.insert(db, txContext, rawContactId, values);
2007d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            if (!txContext.isNewRawContact(rawContactId)) {
2008285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
2009285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
2010a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
2011a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2012a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
2013a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
2014d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
2015d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                Cursor c, boolean callerIsSyncAdapter) {
2016a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
2017d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
2018813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
2019813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
2020813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
2021a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
2022813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
2023a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2024a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
2025a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
2026d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
2027a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
2028d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            int count = super.delete(db, txContext, c);
2029a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
2030a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
2031a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
2032a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
2033a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
2034ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    /**
2035ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
2036ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
2037ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
2038e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    public static class GroupIdCacheEntry {
2039ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
2040ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
2041ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
2042ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
2043ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
2044a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
2045e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
2046e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
2047e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    // is a list of groups with this group id.
2048e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
2049e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov
20503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
2051b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
205231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
20534097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
2054f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
2055315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
2056622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
2057622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
205872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
2059622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
2060f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
2061a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
2062d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
2063a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
206420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
206573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
206620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
206709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
20683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private boolean mProviderStatusUpdateNeeded;
206909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
2070ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
207173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2072d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private TransactionContext mTransactionContext = new TransactionContext();
2073de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
20741a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
20751a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
207681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
207781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
20784cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
20793826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private int mContactsAccountCount;
2080d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
208173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
20824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
20834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
2084de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
2085ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
2086ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
2087ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
2088ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
2089ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
2090ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
2091ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
209235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2093ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
2094de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
2095b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
209672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
2097a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
2098b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
2099653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
210051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
2101d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
21023826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        updateAccounts();
2103bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
210465ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
210565ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov            importLegacyContactsAsync();
210680952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov        } else {
210780952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov            verifyLocale();
210865ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        }
210965ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
211072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        startContactDirectoryManager();
211172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
21122a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        if (isAggregationUpgradeNeeded()) {
21132a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            upgradeAggregationAlgorithm();
21142a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        }
21152a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
21163826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        updateProviderStatus();
21173826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
211849d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        return true;
21194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
21204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2121ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao    private void initDataRowHandlers() {
2122ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers = new HashMap<String, DataRowHandler>();
2123ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao
2124e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov      mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE,
2125e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov              new EmailDataRowHandler(mDbHelper, mContactAggregator));
2126ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
2127e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov              new CommonDataRowHandler(mDbHelper, mContactAggregator,
2128e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                      Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
2129e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov      mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
2130e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov              new CommonDataRowHandler(mDbHelper, mContactAggregator,
2131e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                      StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE,
2132e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                      StructuredPostal.LABEL));
2133e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov      mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE,
2134e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov              new OrganizationDataRowHandler(mDbHelper, mContactAggregator));
2135e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov      mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE,
2136e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov              new PhoneDataRowHandler(mDbHelper, mContactAggregator));
2137e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov      mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE,
2138e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov              new NicknameDataRowHandler(mDbHelper, mContactAggregator));
2139ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
21405df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov              new StructuredNameRowHandler(mDbHelper, mContactAggregator,
21415df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov                      mNameSplitter, mNameLookupBuilder));
2142ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
2143e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov              new StructuredPostalRowHandler(mDbHelper, mContactAggregator, mPostalSplitter));
2144e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov      mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE,
2145e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov              new GroupMembershipRowHandler(mDbHelper, mContactAggregator, mGroupIdCache));
2146e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov      mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE,
2147e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov              new PhotoDataRowHandler(mDbHelper, mContactAggregator));
2148ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao    }
214972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
215051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
2151767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov     * Visible for testing.
2152767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov     */
2153767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
2154767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov        return new PhotoPriorityResolver(context);
2155767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    }
2156767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov
2157767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
215851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
215951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
216004b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
21614cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
216204b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
21634cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
21644cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
216551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
2166cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
21675b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
21685b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                createPhotoPriorityResolver(getContext()), mNameSplitter, mCommonNicknameCache);
21695b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
21705b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
2171ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao        initDataRowHandlers();
21724cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
21734cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
217453fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
21753826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
21763826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
21774f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
21784f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
21794f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
218051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
218151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        verifyLocale();
21824cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
218351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
218451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
218551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
218651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
218751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
218851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
218951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
219051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
219151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void verifyLocale() {
2192f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2193f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
2194f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
2195f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
2196f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
2197f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
219851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
219951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
220051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
220151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
220251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
220351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
220451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
220551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
220651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
220751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
220851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        AsyncTask<Integer, Void, Void> task = new AsyncTask<Integer, Void, Void>() {
220951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
221051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            int savedProviderStatus;
221151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
221251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
221351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected Void doInBackground(Integer... params) {
221451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                savedProviderStatus = params[0];
221551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, currentLocale);
221651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                return null;
221751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
221851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
221951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
222051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected void onPostExecute(Void result) {
22219516b6eef76b3504913f5efcadf603969946a3d0Brad Fitzpatrick                prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
222251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                setProviderStatus(savedProviderStatus);
2223f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2224f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // Recursive invocation, needed to cover the case where locale
2225f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // changes once and then changes again before the db upgrade is completed.
2226f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                verifyLocale();
222751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
222851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        };
222951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
223051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        task.execute(providerStatus);
223151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
223251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
22333826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateProviderStatus() {
22343826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL
22353826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                && mProviderStatus != ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
22363826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return;
22373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
22383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
22393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mContactsAccountCount == 0
224049d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                && DatabaseUtils.queryNumEntries(mDbHelper.getReadableDatabase(),
224149d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                        Tables.CONTACTS, null) == 0) {
22423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
22433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } else {
22443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            setProviderStatus(ProviderStatus.STATUS_NORMAL);
22453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
22463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
22473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
224831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
2249de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
2250b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
2251b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
225231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
225331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
2254013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
2255013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
2256013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
2257013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
22585df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    /* package */ NameLookupBuilder getNameLookupBuilder() {
22595df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov        return mNameLookupBuilder;
22605df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov    }
22615df7e46835c4f103b05407660b4769edd515760fDmitri Plotnikov
22625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
226372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManager() {
226472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
226572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
226672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
226772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
22685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
22695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
22705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
22715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
227272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
227372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    protected void startContactDirectoryManager() {
227472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        getContactDirectoryManager().start();
227572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
227672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
22773d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
2278b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
2279b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
22803d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
22813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2282568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
2283568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
2284568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2285568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2286568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2287568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
2288568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
2289568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2290568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
2291bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
2292bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
2293bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch == null) {
2294bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            mAccessLatch = new CountDownLatch(1);
2295bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2296568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2297568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
2298568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
2299568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
230080952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                final SharedPreferences prefs =
230180952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                    PreferenceManager.getDefaultSharedPreferences(getContext());
230280952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, mCurrentLocale);
230380952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
230480952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov
2305bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                LegacyContactImporter importer = getLegacyContactImporter();
2306bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (importLegacyContacts(importer)) {
2307bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportSuccess();
2308bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2309bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportFailure();
2310568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
2311568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
2312568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
2313568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2314568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
2315568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2316568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2317bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2318bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
2319bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2320bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
2321bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2322bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
2323bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
2324bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2325b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
2326b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
2327b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
2328bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
2329bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch.countDown();
2330bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch = null;
2331bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
2332bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
2333bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2334bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2335bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
2336bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2337bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
2338bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
2339bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2340bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
2341bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2342bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
2343bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
2344bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
2345bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
2346bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
2347bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
2348bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
2349bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
2350bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
2351bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2352bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
2353bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2354bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
2355bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
23563d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
23573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
23583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
2359568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
23600e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
23613d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
23623d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
2363bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
2364bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2365bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
2366bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
2367bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
2368bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
23693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
23703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
23713d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
2372bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
2373bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
23743d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
23753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2376a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
2377a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
2378a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
2379a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
2380b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
23813826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatus = ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS;
2382a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
2383a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2384568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2385568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
2386568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
2387568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
2388568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
2389568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
2390568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2391568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
2392ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
2393ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
2394ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
2395ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
2396ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
2397ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
2398ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
2399ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
240081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
2401ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
2402ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
2403568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
2404568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2405568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2406568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2407568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
2408568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2409568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
2410568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2411568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2412568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2413568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
2414bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch != null) {
2415bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2416bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2417bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2418bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2419bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (match == PROVIDER_STATUS && isLegacyContactImportNeeded()) {
2420bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2421bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2422bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    importLegacyContactsAsync();
2423bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2424bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2425bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2426bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2427bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2428bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2429568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2430568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
2431568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2432568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2433568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2434568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
2435568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2436568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
2437568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2438568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2439568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2440568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2441568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
2442568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2443568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
2444568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2445568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
24464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2447285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
2448bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2449b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2450b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2451285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
24521ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
2453d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
2454b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2455b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2456285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2457285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2458285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
24591129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
2460bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2461b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2462b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2463285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
2464b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
24651ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
24661a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
24671a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
2468b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
24691a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
24703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
24713826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatusUpdateNeeded) {
24723826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
24733826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatusUpdateNeeded = false;
24743826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
2475b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2476b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2477b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2478bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2479b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2480b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
24811129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
2482d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (long rawContactId : mTransactionContext.getInsertedRawContactIds()) {
24837e2635fa663312adb2bc9d04f50a6bb54c6cc5f4Dmitri Plotnikov            mContactAggregator.updateRawContactDisplayName(mDb, rawContactId);
2484d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
2485285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
2486b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2487d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> dirtyRawContacts = mTransactionContext.getDirtyRawContactIds();
2488d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!dirtyRawContacts.isEmpty()) {
2489a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2490a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2491d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, dirtyRawContacts);
2492a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2493a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2494a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2495a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2496d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        Set<Long> updatedRawContacts = mTransactionContext.getUpdatedRawContactIds();
2497d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (!updatedRawContacts.isEmpty()) {
2498a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2499a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2500d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            appendIds(mSb, updatedRawContacts);
2501a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2502a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2503b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2504b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2505d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        for (Map.Entry<Long, Object> entry : mTransactionContext.getUpdatedSyncStates()) {
2506b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
25079d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
25089d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
25099d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
25109d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2511b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2512b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2513d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.clear();
2514b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2515b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2516a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2517a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2518a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2519a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2520d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov    private void appendIds(StringBuilder sb, Set<Long> ids) {
2521b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2522a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2523b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2524a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2525a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2526285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2527285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2528285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2529cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
253081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
253181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
253281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
253381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
253481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
253581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
253681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2537cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2538568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
253951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
25403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (mProviderStatus != status) {
25413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            mProviderStatus = status;
25423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            getContext().getContentResolver().notifyChange(ProviderStatus.CONTENT_URI, null, false);
25433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
254451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
254551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
25463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
25473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
25483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
2549e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov            handler = new CustomDataRowHandler(mDbHelper, mContactAggregator, mimeType);
25503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
25513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
25523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
25533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
25543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
25554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2556de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2557bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
25581129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2559b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2560f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2561f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2562f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2563f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2564a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2565a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
256635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2567a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
256835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2569b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
257035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
257135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2572d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2573d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
25746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
25756bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
25766bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
25775ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2578dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                id = insertRawContact(uri, values, callerIsSyncAdapter);
2579f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2580a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2581a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2582a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
25835ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
25845ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2585f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2586f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2587a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2588a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2589a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2590a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2591f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2592f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2593a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2594a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2595a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2596ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2597f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2598f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2599ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2600ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2601ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2602eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
26035aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
260443880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2605eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2606eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2607eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
260882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
260982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
26101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
26111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
26121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2613a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
261481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2615f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2616a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2617a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
26187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
26197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
26207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
26217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2622de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2623a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2624a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2625a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2626e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2627e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2628e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2629e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2630e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2631e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2632e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2633e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2634e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2635e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2636e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2637e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2638e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
26397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2640e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2641f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2642f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2643e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2644f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2645f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2646f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2647e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2648e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2649e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2650e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2651e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
2652fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2653fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2654e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2655e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2656e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2657e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2658e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2659e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2660e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2661e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2662e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2663e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2664e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2665e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
2666fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2667fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2668e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2669e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2670e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2671f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2672f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2673e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2674f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2675f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2676e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2677e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2678f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2679f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2680e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2681f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2682f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2683f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2684f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2685035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2686f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2687e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
26887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
26897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
26907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2691d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
26926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
26936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
26946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
26956bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2696d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2697de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
26986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
26996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
27006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2701a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2702a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2703f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2704f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2705dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
2706a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2707a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2708dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2709f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2710f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2711f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2712f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2713e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
27147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
27153d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
27163d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2717f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
27183d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
27193d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2720f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2721f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
2722f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2723f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2724f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2725f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2726285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2727285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2728d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactInserted(rawContactId, account);
2729f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2730dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2731dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2732dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2733dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2734dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2735dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2736dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2737dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
27383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
2739023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2740a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2741a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2742dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2743dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2744dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2745dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2746dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2747dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2748dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2749dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2750dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
2751dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
2752dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
2753dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2754dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2755dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2756dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2757dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2758dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2759dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2760dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2761dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2762dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2763dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2764dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2765dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2766dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2767dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2768dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2769dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2770dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2771dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2772dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2773dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2774dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2775dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2776dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2777dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2778dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2779dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2780dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2781dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2782dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2783dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2784dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2785dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2786dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2787dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2788dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2789dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2790dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2791dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2792dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2793dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2794a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2795a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2796a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2797a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2798a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2799a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2800f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2801a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2802de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2803de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
280467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2805de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
280620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2807de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2808de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2809de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2810b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2811de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2812de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2813508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2814de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2815de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2816de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2817de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2818de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
28194097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2820b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2821de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2822a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2823a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2824d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        id = rowHandler.insert(mDb, mTransactionContext, rawContactId, mValues);
2825f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2826d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.markRawContactDirty(rawContactId);
2827a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2828d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        mTransactionContext.rawContactUpdated(rawContactId);
2829a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
28304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
28314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2832ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
28337e2635fa663312adb2bc9d04f50a6bb54c6cc5f4Dmitri Plotnikov        mContactAggregator.updateRawContactDisplayName(db, rawContactId);
2834d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2835d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
28369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
283720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
283820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2839f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
284020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
284120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2842de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2843de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
284414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2845de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2846de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
284714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
284814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2849a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2850d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                count += rowHandler.delete(mDb, mTransactionContext, c);
2851f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
2852d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                    mTransactionContext.markRawContactDirty(rawContactId);
285388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
285420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
285520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2856de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
285720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
285820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
285920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
286020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
286120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
286288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
286388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
286488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
286520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2866f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
286788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
286888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
28694da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
28704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=?",
28714da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2872f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
287320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
287420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
287520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
287620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
287720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
287814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
287920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
288020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
288120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
288220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
288320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
288420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
288520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
288620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
288720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
28887a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
288920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
289020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
289120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2892a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2893d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            return rowHandler.delete(mDb, mTransactionContext, c);
289420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
289520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
289620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
289720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
289820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
289920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2900ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2901ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2902f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2903f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2904f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2905f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2906e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
2907ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2908ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2909f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
291067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2911f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
291267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2913f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2914ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2915dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
2916dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
2917dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
2918dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2919f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2920f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
292173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
292273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2923f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2924ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2925dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
2926dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
2927dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
2928dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
2929dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
2930dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
2931dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
2932dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
2933dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2934dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
2935dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
2936dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
2937dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2938dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
2939dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
2940dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
2941892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
2942892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
2943892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
2944892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
2945892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
2946d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        mTransactionContext.markRawContactDirty(rawContactId);
2947892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
2948dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
2949892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
2950892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
2951dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2952dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2953dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2954f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
29551a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2956ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2957ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2958ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2959ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2960ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
29615aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2962e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
29635aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
29641a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
29651a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2966e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
29671a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
2968e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2969e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2970e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2971ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
297282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
29731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
297482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
297582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
29760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
29774dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
29784dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
29790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
298082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
29814dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
29824dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
29834dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
29844dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
29851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
29861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2987dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
2988dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
298982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
2990f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
29912526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
2992dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
2993dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
2994dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
29952526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
29962526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
29971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
2998dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
2999dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
30010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
30020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
30030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3004dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3005dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3006dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30072a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mDbHelper.getMimeTypeIdForIm());
3008dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
30092a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mDbHelper.getMimeTypeIdForEmail());
3010f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3011f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3012f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3013f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3014f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3015f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3016f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
30172526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
30182526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
30192526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
30202526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
30212526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30222526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
30232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30242526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3025dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
30262526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
30272526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3028dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
30292526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
30302526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3031dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
30322526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
30332526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
30342526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
30352526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30362526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
30372526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3038dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
30392526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
30402526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3041dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3042dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
30431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
304482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
30452526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
30462526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3047dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
304870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
3049f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
305070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
30511f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
30521f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
3053de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
30542526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
30554394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
30561f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
305767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
30585ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
3059e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
30601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
30611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
30621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
30631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
30641f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
306531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
306631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
306731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
30681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
30691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
307082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3071a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3072a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3073a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3074a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3075a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3076a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3077a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
307882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3079a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3080a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
308182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
308282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
308382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
308482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
308582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3086a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
308782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
308882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3089aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3090aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
30911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3092a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
3093a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
3094a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3095e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
30960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
309782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
309882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
30990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
31000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
31010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
31020a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
31030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
31040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
31050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
31060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
31070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
31080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
31090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
31100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3111a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
311278fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.deleteStatusUpdate(dataId);
311382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
311482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
311578fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.replaceStatusUpdate(dataId, timestamp, status, resPackage, iconResource,
311678fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
3117a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
311878fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                mDbHelper.insertStatusUpdate(dataId, status, resPackage, iconResource,
311978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                        labelResource);
3120e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3121e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3122bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3123a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
3124f4015ab9ab7c26b766b5331fbf6655b8c54877eaDmitri Plotnikov            mContactAggregator.updateLastStatusUpdateId(contactId);
3125a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3126a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3127a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
31281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
31291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
31304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3131de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3132bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3133b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3134b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3135b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3136f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3137f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3138508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3139508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
314035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3141b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
314235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3143b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
3144b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3145b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3146b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3147b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
3148b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3149cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3150cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3151cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3152cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3153cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3154d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3155d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3156dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
31576bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
31586bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
31599fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
31602e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
31612e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
31622e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3163fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3164fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
31652e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
31662e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
31672e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3168dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
31692e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
31702e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
31719fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
31729fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
31739fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
31749fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
31759fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
31769fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3177a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
31789fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
31799fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
31809fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
31819fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
31829fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
31839fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
31849fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
31859fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
318660de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
31879fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
31889fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
31899fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
31909fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
31919fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
31929fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3193dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
31949fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
31959fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
31969fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
31979fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
31989fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
31999fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
32009fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
32019fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
32029fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
32039fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
32042971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
32052971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
3206fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
3207fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3208e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
32092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
32102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
32112971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3212fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3213fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3214fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
32152971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
32162971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
32172971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
32182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
32192971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
32202971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
32212971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
32225ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
32232971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
3224fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
3225fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3226508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3227508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
322820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3229f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3230944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3231f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
323220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
323320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
323448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
323548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
323648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
323748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3238508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3239f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
32404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
32414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3242ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3243ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3244ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3245f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
32465aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
32472971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
32482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
32492971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
32502971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
32512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
3252e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
32532971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
32542971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
32555aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
32562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
32572971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
32582971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
32592971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
326081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3261f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
326281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
32632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3264508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3265508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3266eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
326743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3268e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3269eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3270eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
327182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
32720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
32731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
32741f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
327581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
327681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
32773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
327881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3279508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
32804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
32814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
32821c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3283ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3284b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
328594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
3286de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
328794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
328894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
328994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
329094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3291f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
3292de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
329394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
329494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
329594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3296f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
3297de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
329894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
329994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
33001a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
330194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
330294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
330394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
33045aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
3305e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
33061a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3307e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3308e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3309e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3310dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
331196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
3312cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
331396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
331496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3315cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3316cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3317cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3318dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3319cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3320cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3321cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3322cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3323cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
33243826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
33253826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3326cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3327cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3328cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3329fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
33303389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
33313826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mProviderStatusUpdateNeeded = true;
33323826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3333f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
333414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
3335fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
3336fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
3337fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
333833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
3339b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
3340dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
334133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
334233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
334333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
33440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
33459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
33469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
33479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
33489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
33499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
33509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
33519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
33529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
33530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
33540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3355dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
335681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
335781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3358cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3359cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3360cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3361cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3362cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3363cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3364dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3365cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3366cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
33674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3368de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3369de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3370bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3371b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3372b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3373b5a4add17815167d20a90645779df34cdf45280dFred Quintana
337435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
337500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
337600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3377b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3378b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
33791129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3380d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov            mTransactionContext.syncStateUpdated(rowId, data);
3381b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3382b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3383b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3384f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3385f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
338600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
338735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3388b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3389b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3390b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3391b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3392b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3393b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3394b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3395b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3396b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3397b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3398b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
339935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3400d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3401dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
340200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
340300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
340400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3405d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3406dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3407c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3408c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3409c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
34102e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
34112e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
34122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
34132e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
34142e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3415fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3416fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
34172e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
34182e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
34192e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3420dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
34212e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
34222e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
34232e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
34247d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
34257d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
34267d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
34277d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
34287d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
34297d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
34307d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
34317d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
34327d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
34337d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
343420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3435944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3436f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
343781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3438f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
343981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
344020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
344120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3442c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
344348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
344448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
344548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
344648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3447f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
344881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3449f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
345081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
345100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
345200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
34537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
34545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
34555ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3456dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
34577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
34587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
34597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
34605ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
346133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
34624529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
34634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
34644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3465dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3466dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
34674529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
34684da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3469dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3470dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
34714529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
34727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
34737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
34747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3475ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
34765aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3477f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
347881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3479f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
348081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3481ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3482ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3483ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3484ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3485ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
34864da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
34874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
348873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
34895aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
34905aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
349181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3492f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
349381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3494ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3495ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3496ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3497127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3498de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3499b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3500b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3501b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3502eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3503e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3504e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
350543880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3506eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3507eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3508eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
35099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
35109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
35119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
35129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
35139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
351472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
351572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                mContactDirectoryManager.scheduleDirectoryUpdateForCaller();
351672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
3517d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3518d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3519d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
352081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
352181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3522f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
352381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
352400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
352500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
352600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
35274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
35284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
35299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
35309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
35319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
35329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
35339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
35349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
35359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
35369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
35379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
35389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
35399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
35409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
35419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
35439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
35449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
35459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
35469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
35479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
35489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
35499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
35509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
35519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
35529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
35549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
35559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
35569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
35579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
35589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
35599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
35609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
35619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
35629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
35639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
35659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
35669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
35679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
35689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
35699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
35709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
35719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
35729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
35739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
35749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
35759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
35769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
35779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
35789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
35809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
35819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
35829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3583aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3584aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
35859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
35869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
35879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35885aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3589f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
359073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3591ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3592ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
359373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3594f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
359573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
359673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
359773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
359873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
359973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
360073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
360173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
360273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3603ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
36041a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
36051a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
360694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
36076ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
36081129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
36096ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3610e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
36116ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
36126ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
36136ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
36146ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
36156ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
36166ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
36176ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
36186ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
36196ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3620ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
36216ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
36226ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
36236ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
36246ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
36256ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
36266ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
36276ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
36286ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
362994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
363094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
363194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3632b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3633b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3634e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
36351a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
36361a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3637e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3638e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3639e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3640e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3641dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
3642dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
36434529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
36444529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
36454529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
36464529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
364773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
364897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
364997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
365097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
365197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
365297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
36534529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3654b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
365551bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
36564529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
36574529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
36584529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
36594529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
3660dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
36614529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
36624529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
36634529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
36644529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
36654529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
36664529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
36674529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
36684529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
36694529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
3670dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
3671dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
367296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
367396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
367419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
367519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
367619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3677ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3678ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
367919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
368019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
368196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
368219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
368319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
368419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3685ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3686ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
368719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
368819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
368919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
369019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
369119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
369219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
369319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3694f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
369596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
36965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3697f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3698f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3699f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3700f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3701f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3702f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
370369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
3704f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3705f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3706433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
3707dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
3708dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3709dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
3710dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
37114529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3712dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3713dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
3714dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
3715dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
3716dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
3717dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3718dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
3719dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
3720dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
3721dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
3722dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3723dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3724dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3725dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
3726dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3727dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3728dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3729433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3730dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3731285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
37322b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3733285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3734f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3735f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3736f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3737f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3738f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
373978fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov                    mDbHelper.resetNameVerifiedForOtherRawContacts(rawContactId);
3740f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3741f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3742f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
374319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
3744d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                mTransactionContext.rawContactInserted(rawContactId,
3745d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov                        new Account(accountName, accountType));
374619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
37475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
37485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
374933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
375033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3751321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3752f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
375320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
375420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
375520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
37565ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
375720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
375820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
375920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
376020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
376120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3762b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
376320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
376420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
376597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
376697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
376797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
376897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
376997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
3770653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
377120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3772653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3773653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
377414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3775653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3776653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
3777f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
377820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3779653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3780653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
378120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
378220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3783653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
378420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
378520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3786f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3787653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3788653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3789321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3790653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
379114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
3792a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3793d364d74ce9cc677c10362b8686d7c33fafe78bebDmitri Plotnikov        if (rowHandler.update(mDb, mTransactionContext, values, c, callerIsSyncAdapter)) {
3794813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
3795813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
3796813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
3797a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
3798321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3799321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
38008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
3801dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
38028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3803b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
38048c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
38058c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
38068c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
38078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
38088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
3809dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
38108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
38118c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
38128c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
38138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
38148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
38158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
38168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
38178c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
38188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3819dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
3820dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
3821d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
38228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3823b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3824d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3825b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3826d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3827b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3828d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3829b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3830d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3831b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3832d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3833d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3834d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
38358c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3836d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3837d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3838d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
38398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3840c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
38418c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3842c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3843c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
38444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
384597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
384697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
38478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
3848dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
3849dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
3850dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
3851dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
3852dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
3853dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
3854dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
3855dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3856dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
3857dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3858dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
3859dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
3860dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3861dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3862dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
38638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
38648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
38658c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3866b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
38678c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3868b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
38698c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3870b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
38718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3872b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
38738c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3874b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
38758c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
38768c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
38779b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
38786e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
38799b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
38809b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
38819b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
38829b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
38839b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
38849b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
3885f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3886d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3887127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3888127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
38890c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
38900c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
389180c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
38920c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
38930c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
38940c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
38950c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
38960c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
38970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
38980c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3899b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3900127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
39010c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
39024da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
39034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
39040c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
39054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
39064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
39070c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
39086bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
39096bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
39100c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
39110c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
39120c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
39130c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3914127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3915127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
39163389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
391769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
391869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
391969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
392069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
3921dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
39220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1);
39230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2);
3924127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3925127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3926127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3927127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3928b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3929b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
393070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
3931afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        boolean accountsChanged = updateAccounts(accounts);
3932afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        if (accountsChanged) {
39331b2a89588e9593756c2627ce1683539f4ffa1e51Dmitri Plotnikov            mContactDirectoryManager.scheduleScanAllPackages(true);
3934afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        }
3935afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov    }
3936afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov
39373826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected void updateAccounts() {
39383826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
39393826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        Account[] accounts = AccountManager.get(getContext()).getAccounts();
39403826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        updateAccounts(accounts);
39413826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        updateContactsAccountCount(accounts);
39423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
39433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
3944afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov    private boolean updateAccounts(Account[] accounts) {
3945f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
3946e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
3947627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
394849d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
394970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
395070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
3951dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
3952743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
3953743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
3954743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
3955743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
3956e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
3957743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
3958743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
3959743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
3960743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
3961743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
396248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3963627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
3964743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
3965627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
3966627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
3967627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
396870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
396970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
397033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
3971e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
3972e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
3973e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
3974e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
3975e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3976e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
3977e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
3978e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
3979e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3980e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
3981e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
3982e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
3983e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
3984e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3985e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
3986e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3987e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
3988e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3989e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
3990e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3991e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
3992e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
3993e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
3994e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
3995e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
3996e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
3997e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
3998d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
3999d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4000d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
4001d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
40024458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4003e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4004e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
400533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
400633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4007e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
400833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
400933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
401033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
401133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
401269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
401369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
401469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
401533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
401633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
401769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
401869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
401933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
402033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
402133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
402233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
402333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
402433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
402533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
402633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
402733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
402833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
402933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
4030e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
403133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
403233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
4033e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
4034e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
4035e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
403670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
403770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
403870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
403949d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = null;
404070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
404173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
40423826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
40433826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        if (accountsChanged) {
40443826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateContactsAccountCount(accounts);
40453826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            updateProviderStatus();
40463826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
40473826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
4048afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov        return accountsChanged;
404970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4050619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
40513826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    private void updateContactsAccountCount(Account[] accounts) {
40523826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        int count = 0;
40533826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        for (Account account : accounts) {
40543826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            if (isContactsAccount(account)) {
40553826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov                count++;
40563826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            }
40573826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
40583826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        mContactsAccountCount = count;
40593826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
40603826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
40613826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    protected boolean isContactsAccount(Account account) {
40623826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        final IContentService cs = ContentResolver.getContentService();
40633826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        try {
40643826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
40653826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        } catch (RemoteException e) {
40663826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
40673826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov            return false;
40683826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov        }
40693826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov    }
40703826a44d8de41e9c148dd6a967392ea5af478085Dmitri Plotnikov
407172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
407272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager.onPackageChanged(packageName);
4073d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4074d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4075619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
4076627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
4077627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
4078dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
4079743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
4080743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
4081743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
4082627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4083627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
4084dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
4085627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
4086627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4087627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4088627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4089627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4090627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
4091627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4092627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4093627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    /**
4094622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
4095622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
409667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
409767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
409867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
409967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
410067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
410167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
410267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
410367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
410467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
410567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
410667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
410767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
4108dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
4109622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
4110dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
4111dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
4112622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
4113622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
4114dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
4115622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
4116622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
41174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
41184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
41194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
4120d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4121385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directory == null) {
4122385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1);
4123385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directory.equals("0")) {
4124385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder,
4125385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                    Directory.DEFAULT);
4126d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
4127385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder,
4128385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                    Directory.LOCAL_INVISIBLE);
4129d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4130d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4131d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4132d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4133a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            Log.e(TAG, "Invalid directory ID: " + uri);
4134a85745ab25f9ab8fd6fd29e174bf2fac5492e448Dmitri Plotnikov            return null;
4135d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4136d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4137d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4138d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4139d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4140d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4141d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4142d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4143d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4144d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4145d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4146d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
41472e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
41482e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
41492e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
41502e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
41512e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
41522e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4153d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
415409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
415509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
415609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
415709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
415809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4159332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4160d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
4161332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        while (cursor instanceof CursorWrapper) {
4162332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov            cursor = ((CursorWrapper)cursor).getWrappedCursor();
4163332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
4164332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        return cursor;
4165d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4166d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4167d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4168d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4169d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4170d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4171d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4172d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4173d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4174d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4175d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4176d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4177d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4178d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4179d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4180d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4181d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4182d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4183d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4184d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
41854458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
41864458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
41874458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
418849d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
418949d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                Cursor cursor = db.query(Tables.DIRECTORIES,
41904458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
41914458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
41924458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
41934458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
41944458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
41954458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
41964458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
41974458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
41984458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
41994458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
42004458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
42014458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
42024458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4203d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
42044458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4205d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4206d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
42074458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
42084458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4209d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4210d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
421172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
42124458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
42134458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
42144458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
421572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
421672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
4217d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    public Cursor queryLocal(Uri uri, String[] projection, String selection, String[] selectionArgs,
4218385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                String sortOrder, long directoryId) {
4219bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4220bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4221bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
42220b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4223b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
422435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4225d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
42261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4227c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4228c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4229619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
4230619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
4231a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
42324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
423335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4234b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
423535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
423635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4237d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4238763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4239385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
4240619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4241619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4242619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4243d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
42444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4245763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
42464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
42474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
42486bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
42496bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
42506bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
42515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
42525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
42535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
42545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
42555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4256fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4257fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
42585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4259a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
42605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
42615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
42625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
42635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4264763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4265a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4266a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4267a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4268a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4269a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
42705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
42715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
42725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
42735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4274763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
42754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
42764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
42774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
42785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
42795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
42805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42812149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
42822149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
42832149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
42842149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
42852149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
42862149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
42872149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
42882149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
42892149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
42902149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
42912149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
42922149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
42932149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
4294a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4295a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4296a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4297a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
4298a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
42992149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
43002149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
43012149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
43022149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
43032149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
43042149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
43052149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
43062149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
43072149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
43082149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
43092149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
43102149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
43112149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
4312f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4313f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
431442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
4315763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
4316f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
43174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
43184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
43194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4320f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4321f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4322f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
432342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
432442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
432542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
432642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
432742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
432842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
432942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
433042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
433142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
433242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4333ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4334916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4335ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4336916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4337ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
4338916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(qb, uri, projection, filterParam);
4339385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
4340ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4341ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4342ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4343ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4344ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
43454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
4346ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
4347d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
43484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
43494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4350e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
43515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
43524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
43534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
43544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4355763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4356ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
43575e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
43585e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
43595e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
4360dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    starredProjection =
4361dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
4362dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    frequentProjection =
4363dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
43645e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
43655e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
43664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
43674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
43684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4369d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
43705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
43715e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
43724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4373d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4374d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
4375d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
4376763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
43774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
43784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4379d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
43805e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
43815e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
4382d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
4383d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
43844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4385d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4386d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
4387d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
4388d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
43894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
43904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
4391d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
4392d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4393d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
4394d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
4395d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4396d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4397ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4398763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4399b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
440071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
44014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4402b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4403b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4404b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4405b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
4406a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
44074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
440882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
44104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
44116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
44126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
441300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4414a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
44153653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
441682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
44184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
44193653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
44203653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
44213653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
44223653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4423a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
4424a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4425a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4426a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
4427a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
4428a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4429a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4430a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4431a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
4432a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
4433a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
4434a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
4435a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
4436a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4437a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
4438a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4439a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
4440a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
4441a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4442a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4443a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
4444a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4445a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4446a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4447a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4448a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
4449a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
4450a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
4451a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
4452a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
4453a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4454a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4455a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4456a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
4457a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
4458a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
4459a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4460a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4461a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
44624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
446382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
446489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
44652815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
44662815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
44672815f58f72f109790585931f601a63ddc02536a5Evan Millar
446848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
446982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
447148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
44724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
447348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
447448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
447548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4476ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
447782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
447889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
4479ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
44804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
44814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4482a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
44835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
448445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
44855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
44865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
44875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
44885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
44897318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
44905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
449145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
44925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
44935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4494892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
4495892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
44965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
44975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
44985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
44995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
4500892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
4501892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
4502892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
4503892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
4504892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
450545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
450645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
450745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
450845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
450945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
451045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
451145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
45125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
45135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4514a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
4515ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
45165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
4517a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4518a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
4519a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
4520ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4521ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4522ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
45234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
452482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
452589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
45264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
45274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
45284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
452948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
453082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
45324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
45334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
453448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
453548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
453648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
45375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
453882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
453989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
45404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
454108768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
454208768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
454308768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
454408768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
45454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
4546ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4547ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4548ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
45495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
455082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
455107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
455207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
455307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
455407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
455507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
455607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
455707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
45585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
455907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
456007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
456107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
456207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
456307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
456407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
456507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
456607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
456707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
45682a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
45692a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(mDbHelper.getMimeTypeIdForEmail());
45702a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                    sb.append(" AND " + Data.DATA1 + " LIKE ");
457107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
457220938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
457320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
457420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
457507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
457607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            /*
457707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * Using a UNION instead of an "OR" to make SQLite use the right
457807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * indexes. We need it to use the (mimetype,data1) index for the
457907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * email lookup (see above), but not for the name lookup.
458007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * SQLite is not smart enough to use the index on one side of an OR
458107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * but not on the other. Using two separate nested queries
458207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * and a UNION between them does the job.
458307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             */
458407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            sb.append(
458507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " UNION SELECT " + Data._ID +
458607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " FROM " + Tables.DATA +
45872a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                                    " WHERE +" + DataColumns.MIMETYPE_ID + "=");
45882a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            sb.append(mDbHelper.getMimeTypeIdForEmail());
45892a8fefb86282c06a7669f80e1b2b86d87619dfc2Dmitri Plotnikov                            sb.append(" AND " + Data.RAW_CONTACT_ID + " IN ");
45907318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
459120938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
45925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
45935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4594a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
45955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
45965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
4597a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4598c918b0d3ab17a45a392748f43956b927c83eb402Dmitri Plotnikov                    sortOrder = EMAIL_FILTER_SORT_ORDER;
4599a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
46005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
46015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
46025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4603ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
460482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
460589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
460689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4607ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4608ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4609ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
461048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
461182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
461348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
461448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
46154da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
461648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
461748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
461848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
46195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4620763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
46214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
46224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
46234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
46245ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
46255ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
4626763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
46274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
46284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
46294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
46304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
46314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
46325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
46335ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
463482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
46364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
4637e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4638e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4639e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
4640e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
464182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
4642e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4643e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4644e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
46454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
464682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
46484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
4649a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
4650a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
4651a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
4652a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
46534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4654a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
4655a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
4656a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
4657892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
4658a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
4659a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4660e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
4661e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
4662e8b3427e88d28a00cdcad7d296544f2459dfc629Dmitri Plotnikov                        mDbHelper.getCurrentCountryIso());
4663892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
4664892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
4665892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
4666e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
4667e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
4668e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
4669e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
4670a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
4671a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
4672a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4673ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
4674b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4675ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
467689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4677ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4678ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4679ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4680ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
4681b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4682ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
46834da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
46844da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
4685ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4686ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4687ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4688ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
4689b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
4690ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
469189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
469289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
4693ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4694ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4695ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4696b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
46970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
4698b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
4699b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
4700b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
4701b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
470231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
4703d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
47042d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
47052d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
47062d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
47072d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
470831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
4709d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
4710d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
471131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
471231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
471331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
471431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
47155b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
47165b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
47175b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
47185b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
47195b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
47205b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
47215b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
47225b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
472376dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
47245b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
47255b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
47265b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
47275b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
47285b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
47295b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
47305b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
4731763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
47327581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
47337581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
47345b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
473531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
473631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4737eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
4738eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
4739eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
474089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4741e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4742e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
4743e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
4744b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
4745e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
474682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4747b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
4748e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4749e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
475082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4751b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
4752e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4753e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
4754e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4755eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
4756eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
4757eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
475882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
47590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
47605ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
47615ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
47625ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
476382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
47640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
47654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
47664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
47675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
47685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
47695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
4770c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
4771a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
4772c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4773c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4774c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
47752d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
47762d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, lookupKey, projection);
4777c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4778c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
47791b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
4780b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
47811b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
47821b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
47831b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
47841b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
4785b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
47861b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
47871b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
47881b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
47891b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
47901b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
4791b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
47921b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
47931b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
47941b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
47951b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
47961b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
4797b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
47981b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
479971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
48001b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
48011b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
48021b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
480346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
4804a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
480546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
480646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
480746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
480846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
480946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
4810a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
48114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
48124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
481346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
481446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
481546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
481609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
481709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
481809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
481909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4820d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
4821d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4822d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4823d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4824d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4825d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4826d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
4827385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                long id = ContentUris.parseId(uri);
4828d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4829d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4830385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
4831d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
4832d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4833d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4834d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
48357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
48367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
48377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
48387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
48394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
4840f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
4841c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
48424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
48434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
48447f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov        qb.setStrictProjectionMap(true);
48457f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
4846ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
4847ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
4848ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
4849ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
4850ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4851ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
48525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
48535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
48545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
48555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
48565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
4857038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
4858038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
4859038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
4860038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
48615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
48625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
48634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
48644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
48654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
48664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
48674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
48684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
486909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
487009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
487109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
487209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
487309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
487409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
487509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
487609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
487709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
487809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
487909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
488009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
488109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
488209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
488309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
488409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4885a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
4886a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
4887a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
4888a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
4889a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
4890a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
4891a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
4892a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
4893a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
4894a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
4895a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
4896a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
4897a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
4898a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
4899a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
4900a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
4901a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4902a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
4903a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
4904a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
4905a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
4906a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
4907a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
4908a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
4909a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
4910a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4911a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
4912a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
4913a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
491409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4915bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
4916bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
4917bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
4918bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
4919ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4920bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4921bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
4922ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
4923ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4924bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
4925bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
4926bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
4927bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4928de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
4929ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4930ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4931ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
4932ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
4933ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
4934ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
4935ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
4936ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
4937ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
4938ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4939ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
4940ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
4941ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
4942ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
4943ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
4944ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
4945ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
4946ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
4947ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
4948ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
4949ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
4950ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4951ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
4952ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
4953ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4954ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4955bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
4956ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
4957bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
4958bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "SUBSTR(" + sortKey + ",1,1) AS " + AddressBookIndexQuery.LETTER);
4959bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4960bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
4961bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
4962bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
4963bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
4964bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
4965bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
4966bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
4967ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
4968bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "GET_PHONEBOOK_INDEX(SUBSTR(" + sortKey + ",1,1),'" + locale + "')"
4969bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
4970ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
4971ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
4972ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
4973ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4974f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
4975ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
4976ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
4977ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4978ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
4979f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
4980ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
4981ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
4982bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
4983bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
4984bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4985bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
4986bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
4987bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
4988ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
4989f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
4990bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
4991bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
4992bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
4993bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
4994bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
4995bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
4996bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
4997bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
4998bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
4999bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
5000bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5001bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
5002bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
5003bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
5004bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
5005bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5006bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
5007bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
5008bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
5009ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5010ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5011ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            final Bundle bundle = new Bundle();
5012ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
5013f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
5014ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            return new CursorWrapper(cursor) {
5015ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5016ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                @Override
5017ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                public Bundle getExtras() {
5018ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                    return bundle;
5019ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                }
5020ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            };
5021ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
5022f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
5023ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5024ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5025ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
50262d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
502792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
502892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
502992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
503092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
50312d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
50322d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
50335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
50345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
50355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
503692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
503792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
503892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
503992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
504092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
504192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
504292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
504392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
504492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
504592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
504692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
504792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
504892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
504992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
505092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
505192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
505292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
505392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
505492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
50555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
50565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
50575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
50595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
50605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
50625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
50635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
50655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
50665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
50675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
50685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
50695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
50705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
50725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
50735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
50745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
50755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
50765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
50785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
50795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
50805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
50815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
50825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
508392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
50845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
50855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
50865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
50875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
50885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
50895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
50905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
50925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
50935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
50945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
50955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
50965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
50975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
50985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
50995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
51005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
51015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
510292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
510392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
51045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
51055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
51065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
51075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
51085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
51095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
51105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
51115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
51125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
51135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
51155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
51165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
511792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
511892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
51195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
51215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
51225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
51235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
512492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
51255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
51265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
51285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
51295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
513092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
51315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
51325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
513392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
513492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
513592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
513692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
51375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
51385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
513992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
514092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
514192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
51425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
51435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
514492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
514592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
51465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
514792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
514892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
514992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
515092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
515192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
515292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
515392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
515492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
515592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
515692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
515792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
515892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
515992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
516092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
516192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
516292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
516392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
516492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
516592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
516692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
516792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
51685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
51695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
517092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
517192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
517292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
517392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
517492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
517592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
517692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
517792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
517892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
517992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
518092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
518192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
518292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
518392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
518492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
518592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
518692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
518792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
518892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
518992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
519092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
51915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
51925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
51935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
51945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
519592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
519692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
51975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
51985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
51995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
52005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
52025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
52035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
52045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
52065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
52075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
52085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
52095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
52105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
52115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
52125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
52135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
52145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
52155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
521692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
521792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
521892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
52195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
52205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
52215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
52225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
52235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
52245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
52255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
52265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
52275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
52305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
52315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
523292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
523392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
523492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
523592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
523692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
523792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
523892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
523992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
524092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
524192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
524292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
5243ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
5244ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
5245ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
5246ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
52475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
52485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
52495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
52505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
52515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
52525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
52545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
52555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
52575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
52585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
52605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
52615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
52625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
52635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
52645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
52655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
52665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
52675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
52685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
52695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
52705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
52715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
52725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
52735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
52745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
52765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
52775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
52785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
52795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
52815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5282763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5283763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
528482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5285916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5286916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5287916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
5288916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5289916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5290916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
5291916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
5292916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
5293916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
5294916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
5295916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            String[] projection, String filter) {
5296916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5297916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5298916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5299916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5300916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(" JOIN (SELECT " +
5301916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                RawContacts.CONTACT_ID + " AS snippet_contact_id");
5302916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5303916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA_ID)) {
5304916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", " + DataColumns.CONCRETE_ID + " AS "
5305916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    + SearchSnippetColumns.SNIPPET_DATA_ID);
5306916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5307916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
53089c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA1)) {
53099c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA1 + " AS " + SearchSnippetColumns.SNIPPET_DATA1);
5310916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5311916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
53129c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA2)) {
53139c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA2 + " AS " + SearchSnippetColumns.SNIPPET_DATA2);
5314916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5315916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
53169c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA3)) {
53179c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA3 + " AS " + SearchSnippetColumns.SNIPPET_DATA3);
53189c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
53199c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov
53209c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA4)) {
53219c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA4 + " AS " + SearchSnippetColumns.SNIPPET_DATA4);
5322916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5323916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5324916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_MIMETYPE)) {
5325916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", (" +
5326916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    "SELECT " + MimetypesColumns.MIMETYPE +
5327916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " FROM " + Tables.MIMETYPES +
5328916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " WHERE " + MimetypesColumns._ID + "=" + DataColumns.MIMETYPE_ID +
5329916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    ") AS " + SearchSnippetColumns.SNIPPET_MIMETYPE);
5330916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5331916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5332c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS + " WHERE ");
5333c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
5334c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        if (!TextUtils.isEmpty(filter)) {
53356b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov            String normalizedFilter = NameNormalizer.normalize(filter);
53366b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov            if (!TextUtils.isEmpty(normalizedFilter)) {
53376b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                sb.append(DataColumns.CONCRETE_ID + " IN (");
53386b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov
53396b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                // Construct a query that gives us exactly one data _id per matching contact.
53406b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                // MIN stands in for ANY in this context.
53416b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                sb.append(
53426b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        "SELECT MIN(" + Tables.NAME_LOOKUP + "." + NameLookupColumns.DATA_ID + ")" +
53436b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        " FROM " + Tables.NAME_LOOKUP +
53446b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
53456b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        " ON (" + RawContactsColumns.CONCRETE_ID
53466b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                                + "=" + Tables.NAME_LOOKUP + "."
53476b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
53486b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        " WHERE " + NameLookupColumns.NORMALIZED_NAME + " GLOB '");
53496b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                sb.append(normalizedFilter);
53506b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
53516b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                            " IN(" + CONTACT_LOOKUP_NAME_TYPES + ")" +
53526b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        " GROUP BY " + RawContactsColumns.CONCRETE_CONTACT_ID +
53536b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                        ")");
53546b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov            } else {
53556b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov                sb.append("0");     // Empty filter - return an empty set
53566b759a2b977a29c3c4abe1eb6d2ba242f937a923Dmitri Plotnikov            }
5357c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        } else {
5358c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append("0");     // Empty filter - return an empty set
5359c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        }
5360c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
5361c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
5362916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5363916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5364916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
5365916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5366916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5367916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void appendContactsTables(StringBuilder sb, Uri uri, String[] projection) {
5368763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5369f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5370763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5371763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5372d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5373763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5374763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
5375a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
5376a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
537782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
5378ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
5379763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
5380763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
5381763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5382f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5383763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5384763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5385d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5386763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5387763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
5388763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
5389763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
5390763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
5391763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
5392763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5393a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
5394a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(mDbHelper.getRawEntitiesView(shouldExcludeRestrictedData(uri)));
5395a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
539646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
539746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
539846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
539982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
540082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
540182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5402a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getDataView(shouldExcludeRestrictedData(uri)));
540382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
540482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
5405a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
5406a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5407a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5408a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
54093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
541082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
5411f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov
5412f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        boolean useDistinct = distinct
5413f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov                || !mDbHelper.isInProjection(projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
5414f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setDistinct(useDistinct);
5415f4e7ae68ba58d82b16bc2101db8d0f358c1d9297Dmitri Plotnikov        qb.setProjectionMap(useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap);
541682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
5417ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
5418ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
54190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
54200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
54210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5422b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
54230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
5424a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5425a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
54260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5427a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5428a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
5429a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5430a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5431a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
5432a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
5433a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5434a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getEntitiesView(shouldExcludeRestrictedData(uri)));
5435a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
5436a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5437a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
5438a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5439a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
5440a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
5441a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5442a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5443a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
5444a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
5445a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5446a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5447a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
5448a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
5449a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5450a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
5451a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
5452a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
5453a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
5454a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
5455a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
5456a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
5457a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
5458a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
54590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5460a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
54610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5462a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
5463a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5464b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
54650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
54660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
54670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
54680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
54690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
54700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
5471a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
5472a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
54730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5474a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5475a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5476a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
5477a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
5478a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5479a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
5480a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
5481a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
5482a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
5483a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5484a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5485a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5486a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
5487a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5488a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
5489a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
5490a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
5491a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5492a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5493a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5494385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    private void appendLocalDirectorySelectionIfNeeded(SQLiteQueryBuilder qb, long directoryId) {
5495385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        if (directoryId == Directory.DEFAULT) {
5496385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " IN " + Tables.DEFAULT_DIRECTORY);
5497385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        } else if (directoryId == Directory.LOCAL_INVISIBLE){
5498385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov            qb.appendWhere(Contacts._ID + " NOT IN " + Tables.DEFAULT_DIRECTORY);
5499385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov        }
5500385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov    }
5501385182830ff0ed84edce9aba2424d2afe99453ceDmitri Plotnikov
5502a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private boolean shouldExcludeRestrictedData(Uri uri) {
5503a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        // Note: currently, "export only" equals to "restricted", but may not in the future.
5504a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
5505a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.FOR_EXPORT_ONLY, false);
5506a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (excludeRestrictedData) {
5507a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return true;
5508a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5509a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5510a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5511a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5512a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (requestingPackage != null) {
5513a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5514a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5515a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5516a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return false;
55170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
55180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
55194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
5520f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5521f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5522e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5523e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5524e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5525e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5526fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5527fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5528e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5529e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5530e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5531e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5532e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5533e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
55344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
55354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
55364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
55374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
55384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
55394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
55404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
55414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
55424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5543e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
5544f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5545f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5546e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5547e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5548e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5549e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5550fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5551fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5552e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5553e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5554e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5555e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5556e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5557e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
5558e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
5559e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
5560e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
5561e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
5562e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
5563e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
5564e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
5565e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
5566e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
5567e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
5568e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
5569e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
5570e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
5571e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
5572e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
55737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5574c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
5575c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
5576c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
5577c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
5578c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
5579f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
55802e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
5581c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
5582c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5583c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5584c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
5585c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
5586c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
5587c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
5588c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
5589c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
5590c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5591c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
5592c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
5593c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
5594c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5595c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5596c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5597c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
559800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
5599d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
560070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
560170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
5602fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
560370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
560470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
560570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
560670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
5607d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
560870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
560967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
56105ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
56115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
5612619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
5613619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
5614619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5615b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
5616f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
5617b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
5618b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
5619a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
5620f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
5621e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " + RawContacts.CONTACT_ID + "=?",
5622e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5623e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
5624b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5625e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
5626f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return openPhotoAssetFile(uri, mode,
5627e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
56284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5629d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5630d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5631f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
563249d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
563342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
563449d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(lookupContactIdByLookupKey(db, lookupKey));
563542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + "=?";
563642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
563742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
563842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
563942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
564042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
564142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                outputRawContactsAsVCard(localStream, selection, mSelectionArgs1);
5642f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
564342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
564442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
564542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
564649d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                SQLiteDatabase db = mDbHelper.getReadableDatabase();
564742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
564842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
564942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
565042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
5651d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
5652d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
565342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
565442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
5655d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
565642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
5657d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
565842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
565949d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov                    inBuilder.append(lookupContactIdByLookupKey(db, lookupKey));
566042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
566142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
566242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
566342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
5664d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5665d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
5666d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
5667d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
5668d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5669d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                outputRawContactsAsVCard(localStream, selection, null);
5670f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                return buildAssetFileDescriptor(localStream);
5671d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5672b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5673b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
5674fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
5675fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
5676b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
5677b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
5678b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5679f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,
5680e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
5681e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
5682e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
5683e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
5684e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
5685e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
5686e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5687e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
5688e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
5689e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
5690e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
569108ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        try {
5692f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
5693f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs));
569408ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        } catch (SQLiteDoneException e) {
569508ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            // this will happen if the DB query returns no rows (i.e. contact does not exist)
569608ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood            throw new FileNotFoundException(uri.toString());
569708ee3fb4e82900b52d02627ed54907431f4f5adeMathew Inwood        }
5698e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
5699e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5700d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
5701d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5702d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5703f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert     * Returns an {@link AssetFileDescriptor} backed by the
5704d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
5705d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5706f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
5707d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
5708d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
5709d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5710d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
5711d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5712f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert            return makeAssetFileDescriptor(
5713f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME),
5714f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert                    byteData.length);
5715d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
5716ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
5717ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
5718d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5719d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5720d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5721f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd) {
5722f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return makeAssetFileDescriptor(fd, AssetFileDescriptor.UNKNOWN_LENGTH);
5723f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
5724f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
5725f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    private AssetFileDescriptor makeAssetFileDescriptor(ParcelFileDescriptor fd, long length) {
5726f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert        return fd != null ? new AssetFileDescriptor(fd, 0, length) : null;
5727f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert    }
5728f87d3f35a3759d5b95a403c2539859e1b3ee429fBjorn Bringert
5729d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5730d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
5731d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
5732d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
5733d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5734d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
5735d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
5736d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
57377a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
57387a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa                new VCardComposer(context, VCardConfig.VCARD_TYPE_DEFAULT, false);
5739d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
5740d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5741f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
57427a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        if (!composer.init(selection, selectionArgs)) {
57437a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa            Log.w(TAG, "Failed to init VCardComposer");
5744d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
57457a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        }
5746d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5747d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
5748d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
5749d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
5750d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5751d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5752d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
5753d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5754b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
57554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
57564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
5757a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
57584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
5759b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
5760be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
57612d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
5762b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
5763b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
5764b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
5765f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
576642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
5767f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
5768f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
5769f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                return "image/png";
5770b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
5771be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
5772b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
5773b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
5774f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
5775f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
5776508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
5777b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
577848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
577948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
578048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
578148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
57829005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
57839005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
578448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
578548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
578648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
578748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
578848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
578948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
579048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
579148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
5792b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
5793b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
5794b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
5795b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
5796b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
5797b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
5798b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
5799b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
5800c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
5801c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
5802c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
5803c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
5804d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
5805d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
5806d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
5807d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
580861efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
580961efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
58104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
58114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
58127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
581309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
581409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
581509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
581609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
581709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
581809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
581909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
582009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
582109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
582209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
58238727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
58248727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
58258727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
582609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
582709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
582809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
582909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
583009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
583109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
583209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
583309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
583409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
583509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
583609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
583709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
583809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
583909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
584009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
584109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
584209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
584309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
584409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
584509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
584609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
584709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
584809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
584909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
585009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
585109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
585209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
585309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
585409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
585509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
585609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
585709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
585809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
585909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
586009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
586109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
5862f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
5863f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5864f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
5865f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
5866f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5867f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5868f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5869f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
5870f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
587178fb53bfd973760996fe3a5fe260b1d367574de6Dmitri Plotnikov            mDbHelper.insertNameLookup(rawContactId, dataId, lookupType, name);
5872f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5873f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5874f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5875f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
5876d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
5877f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5878f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5879f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
58802d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
5881d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5882d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
5883d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
5884d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
5885d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
5886d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
5887d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
5888e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
5889916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
5890916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
5891e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
5892e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
58935ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
5894c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
58957318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam);
5896c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
5897c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5898c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
58997318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam) {
59007318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), true);
59015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
59025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
59035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
59047318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov            boolean allowEmailMatch) {
590523061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        if (TextUtils.isEmpty(normalizedName)) {
590623061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // Effectively an empty IN clause - SQL syntax does not allow an actual empty list here
590723061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("(0)");
590823061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        } else {
590923061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("(" +
591023061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
591123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " FROM " + Tables.NAME_LOOKUP +
591223061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " WHERE " + NameLookupColumns.NORMALIZED_NAME +
591323061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    " GLOB '");
591423061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // Should not use a "?" argument placeholder here, because
591523061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME.
591623061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append(normalizedName);
591723061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
591823061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_COLLATION_KEY + ","
591923061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NICKNAME + ","
592023061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_SHORTHAND + ","
592123061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.ORGANIZATION + ","
592223061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                    + NameLookupType.NAME_CONSONANTS);
592323061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            if (allowEmailMatch) {
592423061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov                sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
592523061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            }
592623061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov            sb.append("))");
592723061012b777c607100ce30250c3542b4fc8c1c8Dmitri Plotnikov        }
5928ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
5929ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
59304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
59317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
59327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
59337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
59347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
59357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
59367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
59377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
59387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
59397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
59407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
59417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        StructuredNameRowHandler handler =
59427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                (StructuredNameRowHandler) getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
59437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
59447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
59457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
59467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
59477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
59487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
59497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
59507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
59517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
59527a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
59537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
59547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
59557a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
59567a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
59577a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
59587a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
59597a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
59607a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
59617a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
59627a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
59637a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
59647a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
59657a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
59667a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
59677a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
59687a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
59697a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
59707a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
59717a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
59727a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
59737a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
59747a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
59757a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
59767a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
59777a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
59787a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
59794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
59804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
59814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5982b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
5983b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
5984b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
5985b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
5986b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
59874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
59884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5989b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
5990b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5991b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5992caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
59935e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
59945e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
59955e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
59965e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
59975e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
59985e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
59995e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
60005e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
60015e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
60025e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
60035e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
6004caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
6005caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
6006caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
6007df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
6008df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
6009caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
6010caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
6011caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
6012caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
60136f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
6014caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
60156f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
6016caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
6017f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
601873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
601973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
602073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
602173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
6022bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
6023bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
6024bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
6025bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
602673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
602773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
602873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
602973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
603073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
6031627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
6032627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
6033627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
6034627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
603573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
603673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
603773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
6038627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
6039627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
6040627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
6041627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
6042627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
604373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
604473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
604573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
604673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
604773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
604873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
604973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
6050627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
6051b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
6052d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
6053f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
6054f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
6055f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6056f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
6057f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6058f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6059f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6060f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6061f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6062f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
6063f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
6064f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6065f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6066f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6067f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
6068f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6069f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
6070f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
6071f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6072f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6073f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
6074f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
6075f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
6076f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
6077f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
6078f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6079f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6080f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
6081f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
6082f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
6083f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
6084f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6085f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6086f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
6087f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6088f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6089f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
6090f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
6091f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6092f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
6093f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
6094f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
6095f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
6096f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
6097f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
6098f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6099f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6100f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
6101f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6102f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
6103f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
6104f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6105f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6106f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
6107f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
6108f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
6109f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6110f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6111f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6112f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
6113f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
6114f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
6115f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
6116f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
6117f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6118f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6119f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
6120f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
61215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
61220dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
61230dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
61240dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            return false;
61250dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
61260dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
61270dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
61280dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
61290dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
61300dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
61310dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    protected void upgradeAggregationAlgorithm() {
61320dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
61330dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        // main thread during the initial boot after an OTA
61340dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov
61350dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
61360dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        int count = 0;
61370dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
61380dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        try {
613949d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = mDbHelper.getWritableDatabase();
61400dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.beginTransaction();
61410dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Cursor cursor = mDb.query(true,
61420dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
61430dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
61440dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
61450dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
61460dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
61470dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE,
61480dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    null, null, null, null, null);
61490dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            try {
61500dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                while (cursor.moveToNext()) {
61510dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
61520dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
61530dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
61540dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    count++;
61550dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                }
61560dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            } finally {
61570dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                cursor.close();
61580dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            }
61590dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mContactAggregator.aggregateInTransaction(mDb);
61600dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.setTransactionSuccessful();
61610dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
61620dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
61630dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        } finally {
61640dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            mDb.endTransaction();
616549d48c0a709c1efa8593acadadd31350bfc75d9aDmitri Plotnikov            mDb = null;
61660dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
61670dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
61680dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
61690dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov        }
61700dce6bf7a86a78d3073327419f17395c3a2d2688Dmitri Plotnikov    }
61714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
6172