ContactsProvider2.java revision 2d89933b87a15ae5ed5d6b6ec4220ac085695ada
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
193de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.internal.content.SyncStateContentProviderHelper;
203de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
213de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.AggregatedPresenceColumns;
223de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.AggregationExceptionColumns;
233de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.Clauses;
243de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.ContactsColumns;
253de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.DataColumns;
263de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.DisplayNameSources;
273de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.GroupsColumns;
283de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.MimetypesColumns;
293de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.NameLookupColumns;
303de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.NameLookupType;
31f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport com.android.providers.contacts.OpenHelper.NicknameLookupColumns;
323de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.PhoneColumns;
333de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.PhoneLookupColumns;
343de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.PresenceColumns;
353de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.RawContactsColumns;
363de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.SettingsColumns;
373de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.android.providers.contacts.OpenHelper.Tables;
383de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.google.android.collect.Lists;
393de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.google.android.collect.Maps;
403de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport com.google.android.collect.Sets;
413de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
42b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
43caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
4470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wongimport android.accounts.OnAccountsUpdatedListener;
45c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
46568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
47568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
4835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
4967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
5067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
5135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.Entity;
5267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.EntityIterator;
53568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
543d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
5567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
563de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.content.SharedPreferences.Editor;
57b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.content.res.AssetFileDescriptor;
584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
59ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
60b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.database.sqlite.SQLiteContentHelper;
61b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
64c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
66d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.os.MemoryFile;
67b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
680e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
69d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.pim.vcard.VCardComposer;
703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
71508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
723de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
733de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.LiveFolders;
743de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.OpenableColumns;
753de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.SyncStateContract;
76b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
773de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
783de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
793de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
803de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
813de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Presence;
823de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
833de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
85ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
86ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
90de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
91b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
924097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
9367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
94a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
95a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
96f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Token;
97f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport android.text.util.Rfc822Tokenizer;
98c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
100d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
101b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
102d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
103d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
104f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovimport java.lang.ref.SoftReference;
1057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
107b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1080e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
110622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
111b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1120e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
113ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
11970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wongpublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdatedListener {
120caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
121bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
122bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
123bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
125619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
126619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
127619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1313d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
1323d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * Shared preference key for the legacy contact import version. The need for a version
1333d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1343d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1353d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
1363d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final String PREF_CONTACTS_IMPORTED = "contacts_imported_v1";
1373d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private static final int PREF_CONTACTS_IMPORT_VERSION = 1;
1383d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1390e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1400e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
141a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
143d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
144d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.TIMES_CONTACTED + " DESC, "
1459b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
146d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
147d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
148d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
149d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
150d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
151d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
1545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_DATA = 1004;
1555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
1595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_PHOTO = 1009;
1604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1615ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
1625ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
1635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
1644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1656bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1666bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
167ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
16848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
16948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
17048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
17148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
17248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
17348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
17448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
17548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
176a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1776bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
179b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
180b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
181b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1821f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE = 7000;
1831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE_ID = 7001;
1841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
18531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
18631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
187eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
188eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
189ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
190ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
191ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
192ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
19335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
194b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
19535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
196c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
197c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
198c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
199a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov    private static final int DATA_WITH_PRESENCE = 13000;
20019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
2011b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2021b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2031b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2041b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2051b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
20667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    private interface ContactsQuery {
2075ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final String TABLE = Tables.RAW_CONTACTS;
2089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
20967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2106cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2116cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.ACCOUNT_NAME,
2126cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContacts.ACCOUNT_TYPE,
213ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
214ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2155ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
21667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_NAME = 1;
21767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int ACCOUNT_TYPE = 2;
21867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey    }
21967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
220d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
221d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS;
22267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
22367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2246cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
226d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            ContactsColumns.CONCRETE_ID,
2273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.CONCRETE_ID,
228ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
229ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
230d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
23167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
232d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
23367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int MIMETYPE_ID = 3;
234ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private interface DisplayNameQuery {
23767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
2403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
242f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
2433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            StructuredName.DISPLAY_NAME,
2443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
2463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 0;
2473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 1;
248f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 2;
2493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int DISPLAY_NAME = 3;
2503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
25214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
25367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
25588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
2563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2585ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
2593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
260f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
26188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
26288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
26388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
26488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
26588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
26688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
26788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
268f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
2693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
27114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
2723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
2743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
275f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
2763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
27814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
279321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
28020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
28120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
282321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
283321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
28420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
28520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
286f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
287f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private interface NicknameLookupQuery {
288f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String TABLE = Tables.NICKNAME_LOOKUP;
289f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
290f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] COLUMNS = new String[] {
291f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            NicknameLookupColumns.CLUSTER
292f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        };
293f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
294f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int CLUSTER = 0;
295f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
296f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
29725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private static final HashMap<String, Integer> sDisplayNameSources;
2983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    static {
29925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources = new HashMap<String, Integer>();
30025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(StructuredName.CONTENT_ITEM_TYPE,
30125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.STRUCTURED_NAME);
30225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Organization.CONTENT_ITEM_TYPE,
30325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.ORGANIZATION);
30425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Phone.CONTENT_ITEM_TYPE,
30525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.PHONE);
30625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        sDisplayNameSources.put(Email.CONTENT_ITEM_TYPE,
30725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                DisplayNameSources.EMAIL);
3083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
30931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
310caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    public static final String DEFAULT_ACCOUNT_TYPE = "com.google.GAIA";
311df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
312caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
313038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
314038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    private static final HashMap<String, String> sCountProjectionMap;
315e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
3164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
317ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains contacts and presence columns */
318ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    private static final HashMap<String, String> sContactsWithPresenceProjectionMap;
319ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
320d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sRawContactsProjectionMap;
3214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
3224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private static final HashMap<String, String> sDataProjectionMap;
3235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
3245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private static final HashMap<String, String> sDistinctDataProjectionMap;
3259261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
326e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    private static final HashMap<String, String> sPhoneLookupProjectionMap;
327ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
328ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
329ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
330ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
331373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
332b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
333eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
334eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final HashMap<String, String> sSettingsProjectionMap;
335373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains Presence columns */
336373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    private static final HashMap<String, String> sPresenceProjectionMap;
33719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    /** Contains Presence columns */
338a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov    private static final HashMap<String, String> sDataWithPresenceProjectionMap;
3391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
3401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final HashMap<String, String> sLiveFoldersProjectionMap;
3417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
342b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    /** Sql where statement for filtering on groups. */
343d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String sContactsInGroupSelect;
34419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
345c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
346c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
3473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
348c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
349d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    /** Precompiled sql statement for incrementing times contacted for an contact */
350f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private SQLiteStatement mLastTimeContactedUpdate;
3513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
35225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
35373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /** Precompiled sql statement for marking a raw contact as dirty */
35473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private SQLiteStatement mRawContactDirtyUpdate;
355e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    /** Precompiled sql statement for setting an aggregated presence */
356e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    private SQLiteStatement mAggregatedPresenceReplace;
357e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    /** Precompiled sql statement for updating an aggregated presence status */
358e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    private SQLiteStatement mAggregatedPresenceStatusUpdate;
359f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
360f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupDelete;
361a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
3624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
3634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
364a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
365d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
366d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
367d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
3683653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
3693653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3702d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
3712d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
3723653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO);
3735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
3745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
3755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
3765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
377ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
378ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
3795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
3803653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
3815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
3825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
3835ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
384b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
3864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
387ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
38848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
3895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
390ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
3914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
39248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
3935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
3945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
3954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
396ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
39748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
3981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
399ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
400ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
401ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
402ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
40335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
404b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
405b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
40635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
407a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
408b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
409b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
410b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
411b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
4124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
413eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
414eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
415bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence", PRESENCE);
416bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence/#", PRESENCE_ID);
4171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
418c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
419c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
420c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
421c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
422c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/#",
423c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
424c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
4261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
4271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
4281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
4291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
4301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
4311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
4321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
4331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
43419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov        // Private API
435a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data_with_presence", DATA_WITH_PRESENCE);
43619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
43719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
43819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    static {
439038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap = new HashMap<String, String>();
440038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
441e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap = new HashMap<String, String>();
4434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts._ID, Contacts._ID);
4444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
4454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
4464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
4474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
4484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
4494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
4504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
451f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        sContactsProjectionMap.put(Contacts.HAS_PHONE_NUMBER, Contacts.HAS_PHONE_NUMBER);
4524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
4535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sContactsProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
454d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        sContactsProjectionMap.put(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME
455d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                + " || '.vcf' AS " + OpenableColumns.DISPLAY_NAME);
456d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        sContactsProjectionMap.put(OpenableColumns.SIZE, "0 AS " + OpenableColumns.SIZE);
4574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
458ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        sContactsWithPresenceProjectionMap = new HashMap<String, String>();
459ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        sContactsWithPresenceProjectionMap.putAll(sContactsProjectionMap);
460ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        sContactsWithPresenceProjectionMap.put(Contacts.PRESENCE_STATUS,
461e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                Presence.PRESENCE_STATUS + " AS " + Contacts.PRESENCE_STATUS);
462ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        sContactsWithPresenceProjectionMap.put(Contacts.PRESENCE_CUSTOM_STATUS,
463ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Presence.PRESENCE_CUSTOM_STATUS + " AS " + Contacts.PRESENCE_CUSTOM_STATUS);
4644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap = new HashMap<String, String>();
4664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
4674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
4684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
4694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
4704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
4714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
4724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
4734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DELETED, RawContacts.DELETED);
4744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.TIMES_CONTACTED, RawContacts.TIMES_CONTACTED);
4754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.LAST_TIME_CONTACTED,
4764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.LAST_TIME_CONTACTED);
4774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CUSTOM_RINGTONE, RawContacts.CUSTOM_RINGTONE);
4784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SEND_TO_VOICEMAIL, RawContacts.SEND_TO_VOICEMAIL);
4794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.STARRED, RawContacts.STARRED);
4804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
4814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC1, RawContacts.SYNC1);
4824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC2, RawContacts.SYNC2);
4834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC3, RawContacts.SYNC3);
4844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC4, RawContacts.SYNC4);
4852815f58f72f109790585931f601a63ddc02536a5Evan Millar
4864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap = new HashMap<String, String>();
4874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data._ID, Data._ID);
4884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
4894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
4904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
4914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
4924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
4934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
4944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA1, Data.DATA1);
4954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA2, Data.DATA2);
4964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA3, Data.DATA3);
4974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA4, Data.DATA4);
4984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA5, Data.DATA5);
4994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA6, Data.DATA6);
5004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA7, Data.DATA7);
5014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA8, Data.DATA8);
5024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA9, Data.DATA9);
5034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA10, Data.DATA10);
5044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA11, Data.DATA11);
5054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA12, Data.DATA12);
5064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA13, Data.DATA13);
5074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA14, Data.DATA14);
5084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA15, Data.DATA15);
5094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
5104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
5114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
5124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
5134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
5144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
51956d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
5204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
5214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
5224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
5234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
5244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
5254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
5264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
5274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
528a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        // Projection map for data grouped by contact (not raw contact) and some data field(s)
5305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap = new HashMap<String, String>();
5315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data._ID,
5325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                "MIN(" + Data._ID + ") AS " + Data._ID);
5335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
5345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
5355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
5365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
5375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
5385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA1, Data.DATA1);
5395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA2, Data.DATA2);
5405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA3, Data.DATA3);
5415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA4, Data.DATA4);
5425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA5, Data.DATA5);
5435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA6, Data.DATA6);
5445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA7, Data.DATA7);
5455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA8, Data.DATA8);
5465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA9, Data.DATA9);
5475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA10, Data.DATA10);
5485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA11, Data.DATA11);
5495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA12, Data.DATA12);
5505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA13, Data.DATA13);
5515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA14, Data.DATA14);
5525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA15, Data.DATA15);
5535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
5545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
5555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
5565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
5575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
5588f1631f8a610e7278526916ce73ac1e422a5c9b8Jeff Sharkey        sDistinctDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
5595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
5605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
5615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
5625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
5635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
5645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
5655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
5665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID,
5675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID);
5685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
569e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap = new HashMap<String, String>();
570e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
571e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_ID + " AS " + PhoneLookup._ID);
57256d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sPhoneLookupProjectionMap.put(PhoneLookup.LOOKUP_KEY,
57356d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar                Contacts.LOOKUP_KEY + " AS " + PhoneLookup.LOOKUP_KEY);
574e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
575e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_DISPLAY_NAME + " AS " + PhoneLookup.DISPLAY_NAME);
576e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
577e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_LAST_TIME_CONTACTED
578e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
579e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
580e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_TIMES_CONTACTED + " AS " + PhoneLookup.TIMES_CONTACTED);
581e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
582e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_STARRED + " AS " + PhoneLookup.STARRED);
583e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
584e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.IN_VISIBLE_GROUP + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
585e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
586e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.PHOTO_ID + " AS " + PhoneLookup.PHOTO_ID);
587e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
588e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_CUSTOM_RINGTONE + " AS " + PhoneLookup.CUSTOM_RINGTONE);
589e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
590e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Contacts.HAS_PHONE_NUMBER + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
591e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
592e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL
593e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
594e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
595e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
596e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
597e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.TYPE + " AS " + PhoneLookup.TYPE);
598e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
599e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.LABEL + " AS " + PhoneLookup.LABEL);
6009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
601e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        HashMap<String, String> columns;
6027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
603ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
604ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
60589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups._ID, Groups._ID);
606035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
607035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
6089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
6099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
6109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
61189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.RES_PACKAGE, Groups.RES_PACKAGE);
612ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
61367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
614ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
6153cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
61694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        columns.put(Groups.DELETED, Groups.DELETED);
6173cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
61838446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey        columns.put(Groups.SHOULD_SYNC, Groups.SHOULD_SYNC);
61989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC1, Groups.SYNC1);
62089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC2, Groups.SYNC2);
62189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC3, Groups.SYNC3);
62289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC4, Groups.SYNC4);
623ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
624ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
6256cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts and groups projection map
626ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
627ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
628d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
629d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
630ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
631ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
632ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
633d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
634d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
635ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
636f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov                + " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
637ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
638ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
639b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
640b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
641b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
642b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
6430c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID1);
6440c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID2, AggregationExceptions.RAW_CONTACT_ID2);
645b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
646b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
647eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        // Settings projection map
648eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns = new HashMap<String, String>();
649eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
650eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
651eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
652eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
653341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey        columns.put(Settings.ANY_UNSYNCED, "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
654341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + ",(SELECT (CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL THEN 1 ELSE MIN("
655341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Groups.SHOULD_SYNC + ") END) FROM " + Tables.GROUPS + " WHERE "
656fc4e892529eccdfa42121f0304ec7d0dbb42d6c9Dmitri Plotnikov                + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME
657341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
658341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0 THEN 1 ELSE 0 END) AS "
659341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Settings.ANY_UNSYNCED);
66068936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
66168936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " GROUP BY "
66268936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID + " HAVING " + Clauses.HAVING_NO_GROUPS
66368936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + ")) AS " + Settings.UNGROUPED_COUNT);
66468936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
665e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
66668936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Contacts.HAS_PHONE_NUMBER + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
66768936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + " HAVING " + Clauses.HAVING_NO_GROUPS + ")) AS "
66868936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Settings.UNGROUPED_WITH_PHONES);
669eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        sSettingsProjectionMap = columns;
670eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
671373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns = new HashMap<String, String>();
672373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence._ID, Presence._ID);
6734dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(PresenceColumns.RAW_CONTACT_ID, PresenceColumns.RAW_CONTACT_ID);
674373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.DATA_ID, Presence.DATA_ID);
675373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.IM_ACCOUNT, Presence.IM_ACCOUNT);
676373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.IM_HANDLE, Presence.IM_HANDLE);
6774dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(Presence.PROTOCOL, Presence.PROTOCOL);
6784dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(Presence.CUSTOM_PROTOCOL, Presence.CUSTOM_PROTOCOL);
679373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.PRESENCE_STATUS, Presence.PRESENCE_STATUS);
680373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns.put(Presence.PRESENCE_CUSTOM_STATUS, Presence.PRESENCE_CUSTOM_STATUS);
681373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        sPresenceProjectionMap = columns;
682373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov
683a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        sDataWithPresenceProjectionMap = new HashMap<String, String>();
684a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        sDataWithPresenceProjectionMap.putAll(sDataProjectionMap);
685a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        sDataWithPresenceProjectionMap.put(Presence.PRESENCE_STATUS,
686e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                Presence.PRESENCE_STATUS);
687a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        sDataWithPresenceProjectionMap.put(Presence.PRESENCE_CUSTOM_STATUS,
688e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                Presence.PRESENCE_CUSTOM_STATUS);
68919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
6901b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // Live folder projection
6911b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap = new HashMap<String, String>();
6921b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders._ID,
6931b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts._ID + " AS " + LiveFolders._ID);
6941b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders.NAME,
6951b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts.DISPLAY_NAME + " AS " + LiveFolders.NAME);
6961b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
6971b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // TODO: Put contact photo back when we have a way to display a default icon
6981b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // for contacts without a photo
6991b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP,
7001b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        //      Photos.DATA + " AS " + LiveFolders.ICON_BITMAP);
7011b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
7024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsInGroupSelect = Contacts._ID + " IN "
7034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                + "(SELECT " + RawContacts.CONTACT_ID
7044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                + " FROM " + Tables.RAW_CONTACTS
7054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
7064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
7074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        + " FROM " + Tables.DATA_JOIN_MIMETYPES
7084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
7094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                + "' AND " + GroupMembership.GROUP_ROW_ID + "="
7104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                + "(SELECT " + Tables.GROUPS + "." + Groups._ID
7114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                + " FROM " + Tables.GROUPS
7124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                                + " WHERE " + Groups.TITLE + "=?)))";
7134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
7144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
7153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
7163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
7173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
7183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
7193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
721653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
7223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
7243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
7253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
727653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
728653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
729653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                mMimetypeId = mOpenHelper.getMimeTypeId(mMimetype);
730653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
731653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
732653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
733653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
7343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
7353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
7363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
7375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
738e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
739e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
740e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
741e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
742653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
743e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
744e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
745e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
7463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
7493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
7503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
7513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
752653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
753f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
75414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
75514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
756653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
757653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
758653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
759653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
760653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
761653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
762653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
763653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
764653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
765653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
766653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
767653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
768653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
769653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
770653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
771653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
772653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
773653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " = " + dataId, null);
774653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
775653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
776f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
777653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
778653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
7793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
78214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
78314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
78414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
7853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
7863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
7875ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
7883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
7903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7925ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
7935ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long newPrimaryId = findNewPrimaryDataId(db, rawContactId);
7943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (newPrimaryId != -1) {
79514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                setIsPrimary(rawContactId, newPrimaryId, getMimeTypeId());
7963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
7973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
7983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
7995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected long findNewPrimaryDataId(SQLiteDatabase db, long rawContactId) {
800e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
801e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
8025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Cursor c = queryData(db, rawContactId);
8033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
804e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
80514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
806f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
807e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
808e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
809e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
810e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
8113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
8123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
8133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
8143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
815e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return primaryId;
816e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
817e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
818e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
819e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
820e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
821e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
822e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
823e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
8243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        protected Cursor queryData(SQLiteDatabase db, long rawContactId) {
82714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return db.query(DataDeleteQuery.TABLE, DataDeleteQuery.CONCRETE_COLUMNS,
82814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=" + rawContactId +
82914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    " AND " + MimetypesColumns.MIMETYPE + "='" + mMimetype + "'",
8303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    null, null, null, null);
8313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
8323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
83325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
8343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            String bestDisplayName = null;
83525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
83625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov
83767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            Cursor c = db.query(DisplayNameQuery.TABLE, DisplayNameQuery.COLUMNS,
8385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    Data.RAW_CONTACT_ID + "=" + rawContactId, null, null, null, null);
8393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
8403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                while (c.moveToNext()) {
8413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String mimeType = c.getString(DisplayNameQuery.MIMETYPE);
8423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    boolean primary;
8433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    String name;
8443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
8463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        name = c.getString(DisplayNameQuery.DISPLAY_NAME);
8473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        primary = true;
8483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    } else {
849f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                        name = c.getString(DisplayNameQuery.DATA1);
8503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        primary = (c.getInt(DisplayNameQuery.IS_PRIMARY) != 0);
8513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
8523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
85301911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                    if (name != null) {
85425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        Integer source = sDisplayNameSources.get(mimeType);
85501911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                        if (source != null
85601911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                                && (source > bestDisplayNameSource
85701911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                                        || (source == bestDisplayNameSource && primary))) {
85825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                            bestDisplayNameSource = source;
8593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                            bestDisplayName = name;
8603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                        }
8613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                    }
8623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
8633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
8643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
8653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
8663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
8673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
86825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            setDisplayName(rawContactId, bestDisplayName, bestDisplayNameSource);
869285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
870285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateDisplayName(db, rawContactId);
871285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
8723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
873a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
874a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
875a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return true;
876a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
877622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
878622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
879622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
880622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * as baseline, but augmented with any updates.
881622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
882622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
883622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
884622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
885622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=" + dataId,
886622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    null, null, null, null);
887622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
888622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
889622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
890622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
891622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        values.put(key, cursor.getString(i));
892622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
893622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
894622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
895622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
896622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
897622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
898622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
899622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
9003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
9013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
9033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
9053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
9063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
9083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
910622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
9113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
912622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
9133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
914622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
9153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
9185ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
919622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
92014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
92114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
92214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
923f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
924f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name);
92525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
92614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
92714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
92814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
92914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
93014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
931f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
932622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
933622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
934cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
935622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
936622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
93714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
938f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
93914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
940f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME)) {
941f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name = values.getAsString(StructuredName.DISPLAY_NAME);
942f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
943f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name);
94414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
94525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
94614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
94714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
94814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
94914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
95014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
95114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
95214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
95314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
95414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
955f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
95625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
95714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
9583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
961622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
9623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
963622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
964622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
965622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
966622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
9673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
968622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
969622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
970622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
971622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
972622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
973622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
974622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
975622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
976622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
977622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
978622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
979622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
980622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
9818c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
982622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
983622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
984622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } else if (!touchedUnstruct && touchedStruct) {
9858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
986622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
987622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(name);
988622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
989622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
990622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
991622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
992622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
993622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
994622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
995622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
996622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
997622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
998622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
999622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1000622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1001622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1002622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1003622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1004622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1005622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1006622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1007622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1008622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1009f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1010622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1011622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1012622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1013f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1014622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1015622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1016622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1017622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1018622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1019622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1020622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1021622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1022622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1023622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1024622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1025622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1026622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1027622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1028622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1029622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1030622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1031622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
1032622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
1033622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1034622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
1035622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1036622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1037622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1038622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1039622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1040622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1041622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
1042622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } else if (!touchedUnstruct && touchedStruct) {
1043622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1044622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1045622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
10463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
10513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
10533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
10543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
10563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
10573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
10583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
10593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
10625ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1063622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1064622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1065622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
10663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1067622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1068622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1069f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1070622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1071622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1072622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1073f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1074622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
10753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1076622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1077622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1078622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1079622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1080622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1081622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1082622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1083622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
10843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1085622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1086622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1087622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1088622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1089622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
10903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
10943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
10963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
10973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
11005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
11015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            long id = super.insert(db, rawContactId, values);
110225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
11033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return id;
11043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
110714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1108f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
110914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
111014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1111f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
111214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
111325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
111414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
111514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
111614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
111714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
111814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
111914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
112014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
112125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
112214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
112314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
112414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
112514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
11263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
11273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
11283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
11293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
11303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
11313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
11323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
11333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1134a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1135a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1136a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1137a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1138a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
11393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1141e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1142e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1143e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1144e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1145e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1146e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1147e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
11485ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
114914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
115014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
115114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
115214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
115325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1154f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
115514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
115614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
115714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
115814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
115914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1160f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
116114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
116214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
116314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String address = values.getAsString(Email.DATA);
116414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1165f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
116614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1167f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1168f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForEmail(rawContactId, dataId, address);
116925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
117014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
117114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
117214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
117314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
117414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
117514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
117614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
117714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
117814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1179f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
118025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
118114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1182e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1183e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1184e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1185e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1186e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1187e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1188e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1189e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1190e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1191e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1192e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1193e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1194e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1195e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
119614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
119714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
119814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
119914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
120014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
120114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
120214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
120314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
120414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
120514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
120614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
120714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
120825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1209f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
121014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
121114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
121214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
121314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
121414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1215f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
121614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
121714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
121814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
121914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1220f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
122114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1222f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
1223f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameLookupForNickname(rawContactId, dataId, nickname);
122425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
122514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
122614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
122714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
122814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
122914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
123014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
123114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
123214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
123314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1234f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
123525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
123614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
123714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
123814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
123914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
12403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
12413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
12433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
12443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
12480b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
12490b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
12500b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
12510b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1252653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
12530b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1254653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
12550b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1256285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
125725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
12580b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
12590b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
12600b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1261653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1262653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1263653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1264653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1265653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1266f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
126714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
126814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
12690b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
12700b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
12710b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number, values);
1272653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1273f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
1274653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
12750b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1276285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
127725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
12780b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
1279f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                super.update(db, values, c, callerIsSyncAdapter);
12800b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
128114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
128214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
128314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
128414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
128514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
128614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
128714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
128814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
128914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
129014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1291285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
129225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
129314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1294653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1295653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1296653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private String computeNormalizedNumber(String number, ContentValues values) {
1297e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
1298e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1299e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1300e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1301653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1302653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return normalizedNumber;
1303653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1304e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1305653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1306653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                String number, String normalizedNumber) {
1307e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1308653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
13095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1310653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1311e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
1312653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1313653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else {
1314653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=" + dataId, null);
1315e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
13163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
13203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
13213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
13223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
13233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
13243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
13253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
13263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
13273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
13283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
13293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
13303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
13313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1334653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1335653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1336653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1337653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1338653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1339653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1340653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1341653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1342653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
13430be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
13440be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
13450be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1346653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1347653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1348653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1349653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1350f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
135114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1352653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1353f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
13540be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
13550be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
13560be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
13570be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
13580be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
13590be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
13600be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
13610be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
13620be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
13630be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
13640be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
13650be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
13660be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long contactId = mOpenHelper.getContactId(rawContactId);
13670be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
13680be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov                mOpenHelper.updateContactVisible(contactId);
13690be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1370653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1371653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1372653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1373653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1374653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1375653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1376653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1377653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1378653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1379653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1380653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1381653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1382653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1383653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1384653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1385653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1386653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1387653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1388653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1389653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1390653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1391653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1392653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1393653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1394653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId);
1395653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1396653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1397653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1398653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1399a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1400a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1401a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1402a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1403a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1404653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1405653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1406a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1407a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1408a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1409a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1410a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1411a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1412a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1413a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1414a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1415285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1416285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1417285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1418a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1419a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1420a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1421a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1422a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public void update(SQLiteDatabase db, ContentValues values, Cursor c,
1423f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1424a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1425f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1426a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1427a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1428a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1429a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1430a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1431a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1432a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1433a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1434a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1435a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1436a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1437a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1438a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public boolean isAggregationRequired() {
1439a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return false;
1440a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1441a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1442a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1443a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
14443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
144553056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    private final ContactAggregationScheduler mAggregationScheduler;
14464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private OpenHelper mOpenHelper;
144731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
14484097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1449f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1450f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private HashMap<String, SoftReference<String[]>> mNicknameClusterCache =
1451f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            new HashMap<String, SoftReference<String[]>>();
1452622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1453622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1454622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1455f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1456a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1457a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
145820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
145920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1460ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
146173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private boolean mImportMode;
146273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1463b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mInsertedRawContacts = Sets.newHashSet();
1464b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1465b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1466de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
146781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
146881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
1469a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public ContactsProvider2() {
147053056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        this(new ContactAggregationScheduler());
1471a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1472a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1473a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1474a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Constructor for testing.
1475a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
147653056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    /* package */ ContactsProvider2(ContactAggregationScheduler scheduler) {
147753056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mAggregationScheduler = scheduler;
1478a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
14794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
14804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
14814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1482de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
148335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1484de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
1485de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mOpenHelper = (OpenHelper)getOpenHelper();
1486a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1487a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mOpenHelper, this, mGlobalSearchSupport);
1488cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mOpenHelper, mAggregationScheduler);
14890e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1490a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1491d51a83ac4f8032b62d9a23b90a8f43d6b7eb2dbbDmitri Plotnikov        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
1492653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1493c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
1494653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1495653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1496653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1497653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1498653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1499c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
1500653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1501653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1502653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1503653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1504653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1505653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1506653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1507653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1508653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1509653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1510653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
15115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mLastTimeContactedUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET "
15126cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                + RawContacts.TIMES_CONTACTED + "=" + RawContacts.TIMES_CONTACTED + "+1,"
1513d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + RawContacts.LAST_TIME_CONTACTED + "=? WHERE " + RawContacts.CONTACT_ID + "=?");
1514a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
151525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate = db.compileStatement(
151625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
151725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " SET " + RawContactsColumns.DISPLAY_NAME + "=?,"
151825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                        + RawContactsColumns.DISPLAY_NAME_SOURCE + "=?" +
151925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
15203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
152173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET "
152273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                + RawContacts.DIRTY + "=1 WHERE " + RawContacts._ID + "=?");
152373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1524e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        mAggregatedPresenceReplace = db.compileStatement(
1525e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.AGGREGATED_PRESENCE + "("
1526e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                        + AggregatedPresenceColumns.CONTACT_ID + ", "
1527e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                        + Presence.PRESENCE_STATUS
1528e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                + ") VALUES (?, (SELECT MAX(" + Presence.PRESENCE_STATUS + ")"
1529e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                        + " FROM " + Tables.PRESENCE + "," + Tables.RAW_CONTACTS
1530a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                        + " WHERE " + PresenceColumns.RAW_CONTACT_ID + "="
1531e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID
1532e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                        + "   AND " + RawContacts.CONTACT_ID + "=?))");
1533e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1534e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        mAggregatedPresenceStatusUpdate = db.compileStatement(
1535e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                "UPDATE " + Tables.AGGREGATED_PRESENCE
1536e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                + " SET " + Presence.PRESENCE_CUSTOM_STATUS + "=? "
1537e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                + " WHERE " + AggregatedPresenceColumns.CONTACT_ID + "=?");
1538e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1539622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        final Locale locale = Locale.getDefault();
154028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
154128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
154228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
154328f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
1544622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                context.getString(com.android.internal.R.string.common_name_conjunctions),
1545622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                locale);
1546f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
1547622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mPostalSplitter = new PostalSplitter(locale);
15484097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
1549f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert = db.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1550f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1551f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1552f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1553f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete = db.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1554f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1555f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
15563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
15573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1558e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
15593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
15603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
156167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
156267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
15633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
15643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
156514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
15663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
15673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
1568622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
1569622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                new StructuredPostalRowHandler(mPostalSplitter));
1570a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
1571a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
15723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15733d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
1574568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            importLegacyContactsAsync();
15753d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
1576568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1577c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        verifyAccounts();
157870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
15791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
15804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
15814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1582c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
1583c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
1584c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
1585c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
1586c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
158731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
1588de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
158931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    protected OpenHelper getOpenHelper(final Context context) {
159031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        return OpenHelper.getInstance(context);
159131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
159231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1593285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    /* package */ ContactAggregationScheduler getContactAggregationScheduler() {
1594285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        return mAggregationScheduler;
1595285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1596285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1597013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
1598013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
1599013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
1600013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
16013d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
16023d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
16033d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        return prefs.getInt(PREF_CONTACTS_IMPORTED, 0) < PREF_CONTACTS_IMPORT_VERSION;
16043d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
16053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1606568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
1607568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
1608568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1609568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1610568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1611568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
1612568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
1613568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1614568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
1615ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        mAccessLatch = new CountDownLatch(1);
1616568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1617568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
1618568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
1619568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
1620568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                if (importLegacyContacts()) {
1621568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1622568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    /*
1623568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * When the import process is done, we can unlock the provider and
1624568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     * start aggregating the imported contacts asynchronously.
1625568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                     */
1626ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch.countDown();
1627ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1628568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                    scheduleContactAggregation();
1629568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
1630568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
1631568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
1632568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1633568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
1634568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1635568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
16363d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    private boolean importLegacyContacts() {
1637568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        LegacyContactImporter importer = getLegacyContactImporter();
1638568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        if (importLegacyContacts(importer)) {
16393d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
16403d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            Editor editor = prefs.edit();
16413d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.putInt(PREF_CONTACTS_IMPORTED, PREF_CONTACTS_IMPORT_VERSION);
16423d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            editor.commit();
16433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
16443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } else {
16453d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return false;
16463d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
16473d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
16483d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
16493d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
1650568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
16510e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
16523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
165373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mImportMode = true;
16543d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
16553d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            importer.importContacts();
16560e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff            mContactAggregator.setEnabled(aggregatorEnabled);
16573d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            return true;
16583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
16593d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
16603d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           return false;
166173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } finally {
166273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            mImportMode = false;
16633d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
16643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
16653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1666a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
1667a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    protected void finalize() throws Throwable {
1668a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (mContactAggregator != null) {
1669a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.quit();
1670a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
1671a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1672a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        super.finalize();
1673a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1674a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1675a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
1676a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
1677a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
1678a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
1679a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        mOpenHelper.wipeData();
1680a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
1681a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1682568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
1683568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
1684568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
1685568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
1686568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
1687568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
1688568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
1689568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
1690ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
1691ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
1692ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
1693ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
1694ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
1695ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
1696ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
1697ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
169881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
1699ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
1700ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
1701568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
1702568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1703568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1704568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1705568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
1706568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1707568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
1708568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1709568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1710568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1711568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
1712568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1713568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
1714568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1715568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1716568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1717568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
1718568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1719568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
1720568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1721568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1722568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
1723568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1724568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
1725568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
1726568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
1727568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1728568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
17294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1730285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
1731bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1732b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
1733b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1734285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
17351ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
1736b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1737b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1738b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1739b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
1740285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
1741b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
1742df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
1743285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1744285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1745285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1746285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
1747bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1748b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
1749b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1750285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
1751b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
17521ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
1753b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1754b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1755b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
1756bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1757b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
1758b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1759b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long rawContactId : mInsertedRawContacts) {
1760b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mContactAggregator.insertContact(mDb, rawContactId);
1761285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
1762b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1763b5a4add17815167d20a90645779df34cdf45280dFred Quintana        String ids;
1764b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
1765b5a4add17815167d20a90645779df34cdf45280dFred Quintana            ids = buildIdsString(mUpdatedRawContacts);
1766b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mDb.execSQL("UPDATE raw_contacts SET version = version + 1 WHERE _id in " + ids,
1767b5a4add17815167d20a90645779df34cdf45280dFred Quintana                    new Object[]{});
1768b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1769b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1770b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
1771b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
1772b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mOpenHelper.getSyncState().update(mDb, id, entry.getValue());
1773b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1774b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1775b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
1776b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
1777b5a4add17815167d20a90645779df34cdf45280dFred Quintana
1778b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private String buildIdsString(HashSet<Long> ids) {
1779b5a4add17815167d20a90645779df34cdf45280dFred Quintana        StringBuilder idsBuilder = null;
1780b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
1781b5a4add17815167d20a90645779df34cdf45280dFred Quintana            if (idsBuilder == null) {
1782b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder = new StringBuilder();
1783b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder.append("(");
1784b5a4add17815167d20a90645779df34cdf45280dFred Quintana            } else {
1785b5a4add17815167d20a90645779df34cdf45280dFred Quintana                idsBuilder.append(",");
1786b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
1787b5a4add17815167d20a90645779df34cdf45280dFred Quintana            idsBuilder.append(id);
1788b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1789b5a4add17815167d20a90645779df34cdf45280dFred Quintana        idsBuilder.append(")");
1790b5a4add17815167d20a90645779df34cdf45280dFred Quintana        return idsBuilder.toString();
1791285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1792285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1793285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
1794cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
179581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
179681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
179781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
179881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
179981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
180081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
180181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
1802cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
1803568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1804568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected void scheduleContactAggregation() {
1805dee54bb86f3608730f0b9f37d8982a7f6b280a85Dmitri Plotnikov        mContactAggregator.schedule();
1806568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
1807568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
1808285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
1809285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        return mInsertedRawContacts.contains(rawContactId);
1810285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
1811285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
18123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
18133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
18143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
18153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
18163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
18173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
18183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
18193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
18203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
18214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
1822de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
1823bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
1824b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "insertInTransaction: " + uri);
1825b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
1826f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1827f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
1828f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
1829f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
1830a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
1831a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
183235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1833a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
183435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
1835de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                id = mOpenHelper.getSyncState().insert(mDb, values);
183635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
183735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1838d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
1839d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
18406bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
18416bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
18426bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
18435ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
1844f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                final Account account = readAccountFromQueryParams(uri);
1845d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                id = insertRawContact(values, account);
1846f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1847a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1848a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1849a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
18505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
18515ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
1852f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1853f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1854a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1855a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1856a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1857a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
1858f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
1859f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1860a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1861a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1862a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1863ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1864ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final Account account = readAccountFromQueryParams(uri);
1865f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertGroup(values, account, callerIsSyncAdapter);
1866f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
1867ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1868ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1869ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1870eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
1871e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                id = insertSettings(values);
187243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
1873eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
1874eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
1875eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
18761f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
18771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                id = insertPresence(values);
18781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
18791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
18801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1881a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
188281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
1883f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
1884a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1885a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
18867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
18877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
18887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
18897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1890de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
1891a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1892a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1893a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1894035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
1895035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
1896035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param values the ContentValues to read from and update
1897035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param account the explicitly provided Account
1898035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @return false if the accounts are inconsistent
18997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
1900035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private boolean resolveAccount(ContentValues values, Account account) {
1901035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        // If either is specified then both must be specified.
19026cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
19036cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
1904035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (!TextUtils.isEmpty(accountName) || !TextUtils.isEmpty(accountType)) {
1905035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final Account valuesAccount = new Account(accountName, accountType);
1906035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (account != null && !valuesAccount.equals(account)) {
1907035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
1908035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
1909035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            account = valuesAccount;
1910035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1911035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (account != null) {
1912df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            values.put(RawContacts.ACCOUNT_NAME, account.name);
1913df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            values.put(RawContacts.ACCOUNT_TYPE, account.type);
1914035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
1915035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
19167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
19177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
1919d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
19206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
19216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
19226bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
19236bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
1924d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
1925de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
19266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
19276bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
19286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
1929a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
1930a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1931a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1932f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana     * @param account the account this contact should be associated with. may be null.
1933a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1934a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1935d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertRawContact(ContentValues values, Account account) {
1936a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ContentValues overriddenValues = new ContentValues(values);
1937d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        overriddenValues.putNull(RawContacts.CONTACT_ID);
1938f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (!resolveAccount(overriddenValues, account)) {
19397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
19407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
19417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19423d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
19433d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
19443d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov            overriddenValues.put(RawContacts.AGGREGATION_MODE,
19453d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                    RawContacts.AGGREGATION_MODE_DISABLED);
19463d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
19473d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1948023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        long rawContactId =
1949023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov                mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, overriddenValues);
1950023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId);
1951285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
1952285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
1953285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.add(rawContactId);
1954023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
1955a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
1956a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1957a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
1958a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
1959a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
1960a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
1961a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
1962a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
1963f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
1964a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
1965de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
1966de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
196767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
1968de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
196920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
1970de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
1971de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
1972de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
1973de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
1974de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
1975de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
1976508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
1977de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
1978de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
1979de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
1980de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
1981de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
19824097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
1983de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mOpenHelper.getMimeTypeId(mimeType));
1984de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
1985a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1986a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
1987a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
1988f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
1989de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
1990a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
1991b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
1992a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1993a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
1994a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
1995a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1996a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
19974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
19984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
19998e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
20008e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
20018e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
20028e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
20038e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
20048e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        int aggregationMode = mOpenHelper.getAggregationMode(rawContactId);
2005f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
20068e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
20078e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
20088e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
20098e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
2010421782cb554e5050cf62a86b98df6520038dcd15Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId);
2011f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
20128e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
20138e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
20148e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
20158e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                long contactId = mOpenHelper.getContactId(rawContactId);
2016f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
20178e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
20188e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
20198e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2020f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
20218e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2022f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2023c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
20248e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                long contactId = mOpenHelper.getContactId(rawContactId);
20258e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
2026f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
20278e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2028f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2029f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2030f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2031a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
20325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
20339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
20349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
20355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
20369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
20379261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
20389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
20399261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
20409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
20415ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId) {
20429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Account account = null;
20436cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        Cursor c = db.query(ContactsQuery.TABLE, ContactsQuery.PROJECTION, RawContacts._ID + "="
20445ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                + rawContactId, null, null, null, null);
20459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
20469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
204767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountName = c.getString(ContactsQuery.ACCOUNT_NAME);
204867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                final String accountType = c.getString(ContactsQuery.ACCOUNT_TYPE);
20499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
20509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    account = new Account(accountName, accountType);
20519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
20529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
20539261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
20549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
20559261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
20569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
20579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
20589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "has a sourceid the the contact must be associate with "
20599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
20609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
20619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
20629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
20635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
20646cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
20659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2066df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
20679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
20689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
20699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return c.getLong(0);
20709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
20719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2072df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2073df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
20749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
20759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
20769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
20779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
20789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
20799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
20809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return groupId;
20819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
20829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
20839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
20849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
20859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
20869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
20879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
208820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
208920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2090f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
209120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
209220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2093de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2094de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
209514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2096de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2097de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
209814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
209914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2100a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2101a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
2102f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
210388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
2104a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    if (rowHandler.isAggregationRequired()) {
2105a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                        triggerAggregation(rawContactId);
2106a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                    }
210788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
210820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
210920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2110de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
211120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
211220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
211320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
211420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
211520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
211688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
211788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
211888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
211920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2120f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
212188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
212288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
212314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=" + dataId, null,
212414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                null);
2125f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
212620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
212720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
212820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
212920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
213020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
213114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
213220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
213320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
213420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
213520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
213620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
213720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
213820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
213920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
214020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
21417a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
214220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
214320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
214420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2145a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2146a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = rowHandler.delete(mDb, c);
21478e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
2148a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            if (rowHandler.isAggregationRequired()) {
2149a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                triggerAggregation(rawContactId);
2150a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            }
21518e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return count;
215220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
215320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
215420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
215520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
215620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
215720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2158ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2159ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2160f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertGroup(ContentValues values, Account account, boolean callerIsSyncAdapter) {
2161ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ContentValues overriddenValues = new ContentValues(values);
2162ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (!resolveAccount(overriddenValues, account)) {
2163ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
2164ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
2165ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2166ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
216767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        final String packageName = overriddenValues.getAsString(Groups.RES_PACKAGE);
216867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
216967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey            overriddenValues.put(GroupsColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
217067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
217167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        overriddenValues.remove(Groups.RES_PACKAGE);
2172ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2173f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
217473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            overriddenValues.put(Groups.DIRTY, 1);
217573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
217673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2177ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
2178ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2179ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        if (overriddenValues.containsKey(Groups.GROUP_VISIBLE)) {
2180ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey            mOpenHelper.updateAllVisible();
2181ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2182ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2183ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2184ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2185ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2186e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    private long insertSettings(ContentValues values) {
2187e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
2188e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
2189e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            mOpenHelper.updateAllVisible();
2190e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2191e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
2192e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2193e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2194ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
21951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     * Inserts a presence update.
21961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
219770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public long insertPresence(ContentValues values) {
21981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String handle = values.getAsString(Presence.IM_HANDLE);
21994dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        if (TextUtils.isEmpty(handle) || !values.containsKey(Presence.PROTOCOL)) {
22004dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
22014dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        }
22024dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
22034dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        final long protocol = values.getAsLong(Presence.PROTOCOL);
22044dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
22054dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
22064dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        if (protocol == Im.PROTOCOL_CUSTOM) {
22074dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            customProtocol = values.getAsString(Presence.CUSTOM_PROTOCOL);
22084dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
22094dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
22104dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
22114dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
22121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
22131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
22141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // TODO: generalize to allow other providers to match against email
22154dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
22161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
221770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        StringBuilder selection = new StringBuilder();
22181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String[] selectionArgs;
22191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (matchEmail) {
22204dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            selection.append(
22214dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    "((" + MimetypesColumns.MIMETYPE + "='" + Im.CONTENT_ITEM_TYPE + "'"
22224dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + " AND " + Im.PROTOCOL + "=?"
22234dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + " AND " + Im.DATA + "=?");
22244dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (customProtocol != null) {
22254dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                selection.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
22264dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(selection, customProtocol);
22274dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
22284dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            selection.append(") OR ("
22294dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + MimetypesColumns.MIMETYPE + "='" + Email.CONTENT_ITEM_TYPE + "'"
22304dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + " AND " + Email.DATA + "=?"
22314dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + "))");
22324dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            selectionArgs = new String[] { String.valueOf(protocol), handle, handle };
22331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
22344dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            selection.append(
22354dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    MimetypesColumns.MIMETYPE + "='" + Im.CONTENT_ITEM_TYPE + "'"
22364dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + " AND " + Im.PROTOCOL + "=?"
22374dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                    + " AND " + Im.DATA + "=?");
22384dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (customProtocol != null) {
22394dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                selection.append(" AND " + Im.CUSTOM_PROTOCOL + "=");
22404dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(selection, customProtocol);
22414dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
22424dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
22434dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            selectionArgs = new String[] { String.valueOf(protocol), handle };
22441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
22451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
224670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (values.containsKey(Presence.DATA_ID)) {
224770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            selection.append(" AND " + DataColumns.CONCRETE_ID + "=")
224870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    .append(values.getAsLong(Presence.DATA_ID));
224970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
225070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
225100ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey        selection.append(" AND ").append(getContactsRestrictions());
225270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
22531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long dataId = -1;
22545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        long rawContactId = -1;
2255e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        long contactId = -1;
225670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
22571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
22581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
2259de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
226070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov                    selection.toString(), selectionArgs, null, null, null);
22611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
226267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
22635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
2264e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
22651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
22661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
22671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
22681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
22691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
227031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
227131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
227231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
22731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
22741f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
22751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.DATA_ID, dataId);
22764dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        values.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
22771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
22781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Insert the presence update
2279de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long presenceId = mDb.replace(Tables.PRESENCE, null, values);
2280e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
2281e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        if (contactId != -1) {
2282e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            if (values.containsKey(Presence.PRESENCE_STATUS)) {
2283e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                mAggregatedPresenceReplace.bindLong(1, contactId);
2284e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                mAggregatedPresenceReplace.bindLong(2, contactId);
2285e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                mAggregatedPresenceReplace.execute();
2286e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2287e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            String status = values.getAsString(Presence.PRESENCE_CUSTOM_STATUS);
2288e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            if (status != null) {
2289e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                mAggregatedPresenceStatusUpdate.bindString(1, status);
2290e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                mAggregatedPresenceStatusUpdate.bindLong(2, contactId);
2291e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                mAggregatedPresenceStatusUpdate.execute();
2292e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
2293e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
22941f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return presenceId;
22951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
22961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
22974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2298de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
2299bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2300b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
2301b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2302b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2303f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2304f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2305508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
2306508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
230735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2308de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mOpenHelper.getSyncState().delete(mDb, selection, selectionArgs);
230935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2310b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
2311b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2312b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2313b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2314b5a4add17815167d20a90645779df34cdf45280dFred Quintana                return mOpenHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
2315b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2316cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
2317cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
2318cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
2319cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2320cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2321d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
2322d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2323cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return deleteContact(contactId);
23246bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
23256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
23262e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
23272e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
23282e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
23292e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
23302e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
23312e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
23322e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
23332e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
23342e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
23352e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                return deleteContact(contactId);
23362e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
23372e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
23382971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
23392971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
23402971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2341e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
23422971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
23432971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
23442971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
2345f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        numDeletes += deleteRawContact(rawContactId, callerIsSyncAdapter);
23462971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
23472971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
23482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
23492971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
23502971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
23512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
23522971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
23535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
23542971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
2355f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteRawContact(rawContactId, callerIsSyncAdapter);
2356508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2357508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
235820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2359f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2360944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
2361f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
236220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
236320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
236448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
236548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
236648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
236748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2368508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
2369f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2370f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteData(Data._ID + "=" + dataId, null, callerIsSyncAdapter);
2371ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2372ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2373ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2374f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2375f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                return deleteGroup(ContentUris.parseId(uri), callerIsSyncAdapter);
23762971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
23772971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
23782971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
23792971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
23802971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
2381e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
23822971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
23832971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
2384f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        numDeletes += deleteGroup(c.getLong(0), callerIsSyncAdapter);
23852971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
23862971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
23872971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
23882971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
238981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
2390f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
239181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
23922971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
2393508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
2394508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2395eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
239643880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2397e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                return deleteSettings(selection, selectionArgs);
2398eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2399eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
24001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
2401eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
24021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
24031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
240481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
240581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
24063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
240781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
2408508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
24094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
24104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
24112971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana    private boolean readBooleanQueryParameter(Uri uri, String name, boolean defaultValue) {
24122971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        final String flag = uri.getQueryParameter(name);
24132971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana        return flag == null
24142971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                ? defaultValue
24152971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                : (!"false".equals(flag.toLowerCase()) && !"0".equals(flag.toLowerCase()));
241694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
241794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
2418f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteGroup(long groupId, boolean callerIsSyncAdapter) {
241994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        final long groupMembershipMimetypeId = mOpenHelper
242094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
2421de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
242294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
242394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
242494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
242594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
2426f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
2427de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
242894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
242994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
243094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
2431f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
2432de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
243394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
243494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
243594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            mOpenHelper.updateAllVisible();
243694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
243794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
243894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
2439e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    private int deleteSettings(String selection, String[] selectionArgs) {
2440e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
2441e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        if (count > 0) {
2442e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            mOpenHelper.updateAllVisible();
2443e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2444e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2445e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2446e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
2447cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int deleteContact(long contactId) {
2448cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
2449cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                RawContacts.CONTACT_ID + "=" + contactId, null, null, null, null);
2450cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
2451cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
2452cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
2453cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                markRawContactAsDeleted(rawContactId);
2454cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
2455cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
2456cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
2457cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
2458cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2459cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
2460cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2461cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2462f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    public int deleteRawContact(long rawContactId, boolean callerIsSyncAdapter) {
2463f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
246414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
2465de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            return mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
246633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
2467a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov            mOpenHelper.removeContactIfSingleton(rawContactId);
2468cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return markRawContactAsDeleted(rawContactId);
246933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
247033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
247133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
2472cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int markRawContactAsDeleted(long rawContactId) {
247381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
247481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
2475cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
2476cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
2477cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
2478cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
2479cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2480cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
2481cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return updateRawContact(rawContactId, mValues);
2482cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
2483cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
2484f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private static Account readAccountFromQueryParams(Uri uri) {
24856cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String name = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
24866cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        final String type = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
2487f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(type)) {
2488f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            return null;
2489f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        }
2490f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        return new Account(name, type);
2491f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    }
2492f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
24934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2494de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
2495de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
2496bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2497b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
2498b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2499b5a4add17815167d20a90645779df34cdf45280dFred Quintana
250035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
250100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
250200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
2503b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
2504b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
2505b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Object data = values.get(ContactsContract.SyncStateColumns.DATA);
2506b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
2507b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
2508b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2509b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
2510f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2511f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
251200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
251335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2514b5a4add17815167d20a90645779df34cdf45280dFred Quintana                return mOpenHelper.getSyncState().update(mDb, values,
2515b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
2516b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2517b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
2518b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
2519b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
2520b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
2521b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
2522b5a4add17815167d20a90645779df34cdf45280dFred Quintana                return mOpenHelper.getSyncState().update(mDb, values,
2523b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
2524b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
252535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2526d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
25278c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(values, selection, selectionArgs);
252800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
252900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
253000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
2531d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
25328c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(ContentUris.parseId(uri), values);
2533c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
2534c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
2535c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
25362e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
25372e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
25382e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
25392e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
25402e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
25412e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
25422e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
25432e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
25442e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
25458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(contactId, values);
25462e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
25472e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
25482e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
254920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
2550944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
2551f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
255281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2553f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
255481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
255520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
255620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2557c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
255848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
255948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
256048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
256148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
2562f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
256381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2564f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
256581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
256600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
256700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
25687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25695ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
25704529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count = updateRawContacts(values, selection, selectionArgs);
25717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
25727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
25737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
257533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
25764529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
25774529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=" + rawContactId
25784529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                                    + " AND(" + selection + ")", selectionArgs);
25794529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
25804529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    count = updateRawContact(rawContactId, values);
25814529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
25827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
25837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
25847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2585ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2586e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                count = updateGroups(values, appendAccountToSelection(uri, selection),
2587f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
258881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2589f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
259081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2591ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2592ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2593ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2594ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
2595ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
259673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                String selectionWithId = (Groups._ID + "=" + groupId + " ")
259773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
2598f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateGroups(values, selectionWithId, selectionArgs, callerIsSyncAdapter);
259981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
2600f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
260181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
2602ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2603ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2604ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2605127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
2606de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
2607b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
2608b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
2609b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2610eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
2611e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                count = updateSettings(values, selection, selectionArgs);
261243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2613eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2614eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2615eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
261681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
261781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2618f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
261981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
262000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
262100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
262200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
26234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
26244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2625de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    private int updateGroups(ContentValues values, String selectionWithId,
2626f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
262773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
262873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
2629f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
263073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
263173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
263273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
263373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
263473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
263573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
263673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
263773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2638ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
263994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
264094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        // If changing visibility, then update contacts
2641ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
264294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            mOpenHelper.updateAllVisible();
264394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
264494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
264594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
264694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
2647e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    private int updateSettings(ContentValues values, String selection, String[] selectionArgs) {
2648e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
2649e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
2650e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey            mOpenHelper.updateAllVisible();
2651e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
2652e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
2653e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
2654e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
26554529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs) {
26564529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
26574529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
26584529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
26594529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
266073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
26614529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
26624529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        Cursor cursor = mDb.query(mOpenHelper.getRawContactView(),
266351bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
26644529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
26654529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
26664529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
26674529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
26684529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                updateRawContact(rawContactId, values);
26694529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
26704529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
26714529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
26724529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
26734529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
26744529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
26754529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
26764529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
26774529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
26784529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContact(long rawContactId, ContentValues values) {
26794529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = mDb.update(Tables.RAW_CONTACTS, values, RawContacts._ID + " = " + rawContactId,
26804529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                null);
26815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
2682433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
26834529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
2684433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
2685285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
2686285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateLookupKey(mDb, rawContactId);
2687285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
26885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
26895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
269033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
269133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
2692321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
2693f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
269420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
269520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
269620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
26975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
269820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
269920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
270020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
270120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
270220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
270320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
270420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
270520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
270670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
270770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
270820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
270920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
271020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
271170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
271220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
271370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
271420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
271570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
271620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
271770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
271820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
271920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2720653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
272120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2722653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2723653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
272414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
2725653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
2726653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
2727f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
272820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
2729653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
2730653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
273120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
273220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2733653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
273420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
273520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2736f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
2737653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
2738653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
2739321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
2740653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
274114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
2742a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2743f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        rowHandler.update(mDb, values, c, callerIsSyncAdapter);
27448e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
2745a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        if (rowHandler.isAggregationRequired()) {
2746a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            triggerAggregation(rawContactId);
2747a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
27488e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2749653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return 1;
2750321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
2751321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
27528c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
27538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            String[] selectionArgs) {
27548c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
27558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        Cursor cursor = mDb.query(mOpenHelper.getContactView(),
27568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
27578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
27588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
27598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
27608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
27618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                updateContactOptions(contactId, values);
27628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
27638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
27648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
27658c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
27668c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
27678c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
27688c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
27698c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
27708c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
27718c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(long contactId, ContentValues values) {
2772d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
27738c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
27748c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
2775d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
27768c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
2777d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
27788c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
2779d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
27808c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
2781d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
27828c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.STARRED,
2783d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
2784d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2785d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
27868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
2787d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
2788d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
2789d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
27908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
2791c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
27928c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
2793c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
2794c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
27958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=" + contactId, null);
27968c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
27978c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
27988c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
27998c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
28008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
28018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
28028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
28038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
28048c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
28058c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
28068c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
28078c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
28088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        OpenHelper.copyLongValue(mValues, RawContacts.STARRED,
28098c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
28108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
28118c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=" + contactId, null);
2812f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2813d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2814d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    public void updateContactTime(long contactId, long lastTimeContacted) {
2815f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
2816d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(2, contactId);
2817f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.execute();
2818d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
2819d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
2820127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
2821127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
28220c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
28230c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
282480c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
28250c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
28260c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
28270c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
28280c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
28290c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
28300c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
28310c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
2832b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
2833127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
28340c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
28350c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
28360c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=" + rawContactId1 + " AND "
28370c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=" + rawContactId2, null);
28380c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
28396bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
28406bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
28410c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
28420c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
28430c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
28440c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
2845127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
2846127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
2847dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1);
2848dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2);
2849dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
28500c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long contactId1 = mOpenHelper.getContactId(rawContactId1);
28510c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
28520c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
28530c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long contactId2 = mOpenHelper.getContactId(rawContactId2);
28540c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
2855127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
2856127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
2857127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
2858127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
2859b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
2860b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
286170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
286270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb = mOpenHelper.getWritableDatabase();
286370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        if (mDb == null) return;
286470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
286570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        Set<Account> validAccounts = Sets.newHashSet();
286670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        for (Account account : accounts) {
286770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            validAccounts.add(new Account(account.name, account.type));
286870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
286970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        ArrayList<Account> accountsToDelete = new ArrayList<Account>();
287070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
287170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
287270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
287348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
28745f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana            for (String table : new String[]{Tables.RAW_CONTACTS, Tables.GROUPS, Tables.SETTINGS}) {
28755f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                // Find all the accounts the contacts DB knows about, mark the ones that aren't
28765f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                // in the valid set for deletion.
28775f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Cursor c = mDb.rawQuery("SELECT DISTINCT account_name, account_type from "
28785f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        + table, null);
28795f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                while (c.moveToNext()) {
28805f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                    if (c.getString(0) != null && c.getString(1) != null) {
28815f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        Account currAccount = new Account(c.getString(0), c.getString(1));
28825f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        if (!validAccounts.contains(currAccount)) {
28835f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                            accountsToDelete.add(currAccount);
28845f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        }
288570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                    }
288670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                }
28875f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                c.close();
288870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
288970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
289070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            for (Account account : accountsToDelete) {
28915f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Log.d(TAG, "removing data for removed account " + account);
289270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                String[] params = new String[]{account.name, account.type};
289370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                mDb.execSQL("DELETE FROM " + Tables.GROUPS
289470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE account_name = ? AND account_type = ?", params);
289570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                mDb.execSQL("DELETE FROM " + Tables.PRESENCE
289670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (SELECT "
289770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + RawContacts._ID + " FROM " + Tables.RAW_CONTACTS
289870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE account_name = ? AND account_type = ?)", params);
289970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                mDb.execSQL("DELETE FROM " + Tables.RAW_CONTACTS
290070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                        + " WHERE account_name = ? AND account_type = ?", params);
29015f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                mDb.execSQL("DELETE FROM " + Tables.SETTINGS
29025f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                        + " WHERE account_name = ? AND account_type = ?", params);
290370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
29044637c20c6b6d3b6f6671e6a44ed57f1e5b9c4484Dmitri Plotnikov            mOpenHelper.getSyncState().onAccountsChanged(mDb, accounts);
290570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
290670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
290770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
290870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
290970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
2910619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2911619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
2912622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
2913622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
2914622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private static boolean areAllEmpty(ContentValues values, String[] keys) {
2915622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
2916622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (!TextUtils.isEmpty(values.getAsString(key))) {
2917622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                return false;
2918622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
2919622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
2920622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        return true;
2921622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
2922622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
29234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
29244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
29254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
2926bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2927bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
2928bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
29290b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
29304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
293135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2932d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
29331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
2934c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
2935c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2936619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
2937619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
2938a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
29394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
294035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
294135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().query(db, projection, selection,  selectionArgs,
294235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
294335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2944d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2945ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
2946619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
2947619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2948619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2949d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
29504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
2951ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
29524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(Contacts._ID + "=" + contactId);
29536bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
29546bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
29556bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
29565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
29575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
29585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
29595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
29605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
29615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    throw new IllegalArgumentException("URI " + uri + " is missing a lookup key");
29625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
29635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
29645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
29655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
29665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
29675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    setTablesAndProjectionMapForContacts(lookupQb, projection);
29685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    lookupQb.appendWhere(Contacts._ID + "=" + contactId + " AND " +
29695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            Contacts.LOOKUP_KEY + "=");
29705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    lookupQb.appendWhereEscapeString(lookupKey);
29715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    Cursor c = query(db, lookupQb, projection, selection, selectionArgs, sortOrder,
29725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            groupBy, limit);
29735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (c.getCount() != 0) {
29745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
29755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
29765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
29775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    c.close();
29785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
29795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
29805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
29815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=" + lookupContactIdByLookupKey(db, lookupKey));
29825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
29835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
29845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
2985ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
2986ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
2987ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
29884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
29894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
2990e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
29915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
29924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(sb.toString());
2993ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
2994ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
2995ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
2996ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2997ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
2998ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
29994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
3000ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
3001d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
30024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
30034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
3004e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
30055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
30064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
30074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
30084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3009ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
3010ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
30114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
30124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
30134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3014d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3015d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                final String starredQuery = qb.buildQuery(projection, Contacts.STARRED + "=1",
30164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3017d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3018d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
3019d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
3020ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
30214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
30224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
3023d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3024d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String frequentQuery = qb.buildQuery(projection,
3025d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
3026d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
30274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
3028d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3029d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
3030d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
3031d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
30324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
30334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
3034d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
3035d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
3036d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
3037d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
3038d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
3039d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
3040ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
3041ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
3042b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
30434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(sContactsInGroupSelect);
30444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
3045b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
3046b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
3047b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
3048b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
3049d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_DATA: {
30504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
30514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
30524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
30534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
30544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
30554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=" + contactId);
30566bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
30576bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
305800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3059ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_PHOTO: {
30603653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
30613653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
30623653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
30633653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
30643653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
30653653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=" + contactId);
30663653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
30673653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
30683653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
30693653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
30704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
30714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
30724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
307389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
307489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
30752815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
30762815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
30772815f58f72f109790585931f601a63ddc02536a5Evan Millar
307848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
307948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
308048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
308148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
308248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
308348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
308448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
308548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
308648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3087ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
30884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
30895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                qb.setProjectionMap(sDistinctDataProjectionMap);
309089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
309189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
3092ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
30934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
30944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
30955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append("(");
30965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
30975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
30985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
30995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
31005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
31015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, null);
31025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
31035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
31045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
31055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (isPhoneNumber(filterParam)) {
31065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
31075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
31085e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
31095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
31105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
31115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
31125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
31135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " FROM " + Tables.PHONE_LOOKUP
31145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
31155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(reversed);
31165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append("')");
31175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
31185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
31194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(" AND " + sb);
3120ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
31215e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
3122ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3123ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3124ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
31264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
31274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
312889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
312989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
31304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
31314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
31324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
313348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
313448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
313548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
313648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
313748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
313848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
313948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
314048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
314148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
31425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
31434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
31444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
314589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
314689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
31474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
31485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    qb.appendWhere(" AND " + Email.DATA + "=");
31494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhereEscapeString(uri.getLastPathSegment());
31504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
3151ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3152ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3153ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
31545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
31555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
31565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                qb.setProjectionMap(sDistinctDataProjectionMap);
315789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
315889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
31595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
31605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
31615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
31625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append("(");
31635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
31645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
31655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
31665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
31675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, null);
31685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(" OR ");
31695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
31705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
31715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(Email.DATA + " LIKE ");
31721e530df9f7e496dc47f77d4323c89bd413b79b64Dmitri Plotnikov                    sb.append(DatabaseUtils.sqlEscapeString(filterParam + '%'));
31735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
31745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    qb.appendWhere(" AND " + sb);
31755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
31765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
31775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
31785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
31795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3180ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
31814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
31824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
318389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
318489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
318589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
3186ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
3187ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
3188ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
318948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
319048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
319148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
319248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
319348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
319448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
319548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + uri.getLastPathSegment());
319648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
319748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
319848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
31995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
32004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getRawContactView());
3201d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sRawContactsProjectionMap);
320289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
32034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
32044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
32054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
32065ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
32075ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
32084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getRawContactView());
3209d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                qb.setProjectionMap(sRawContactsProjectionMap);
321089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
321189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=" + rawContactId);
32124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
32134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
32144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
32155ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
32165ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
32174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
32184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
321989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
322089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=" + rawContactId);
3221e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3222e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3223e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
3224e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
32254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
32264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
32274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3228e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
3229e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
3230e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
32314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
32324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setTables(mOpenHelper.getDataView());
32334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.setProjectionMap(sDataProjectionMap);
32344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                qb.appendWhere(Data._ID + "=" + ContentUris.parseId(uri));
32354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
32364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
32374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
3238a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            case DATA_WITH_PRESENCE: {
3239a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                qb.setTables(mOpenHelper.getDataView() + " data"
3240e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                        + " LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE
3241e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                        + " ON (" + AggregatedPresenceColumns.CONTACT_ID + "="
3242e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                                + RawContacts.CONTACT_ID + ")");
3243a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                qb.setProjectionMap(sDataWithPresenceProjectionMap);
32445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3245a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
3246a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
3247a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
3248a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
32494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3250a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
3251a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
3252a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
3253e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
3254a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
3255a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3256e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
3257e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                mOpenHelper.buildPhoneLookupAndContactQuery(qb, number);
3258e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
3259e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov
3260e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
3261e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
3262e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
3263a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
3264a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
3265a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3266ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
326789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.setTables(mOpenHelper.getGroupView());
3268ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
326989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3270ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3271ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3272ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3273ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3274ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
327589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.setTables(mOpenHelper.getGroupView());
3276ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
327789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(Groups._ID + "=" + groupId);
3278ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3279ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3280ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3281ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
328289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.setTables(mOpenHelper.getGroupView() + " AS groups");
3283ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
328489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
328589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
3286ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3287ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3288ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3289b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
32900c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
3291b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
3292b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3293b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3294b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
329531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
3296d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
32972d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
32982d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
32992d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
33002d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
330131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
3302d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
3303d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
330431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
330531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
330631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
330731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
33087581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                setTablesAndProjectionMapForContacts(qb, projection);
33097581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
33107581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
33112d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        maxSuggestions, filter);
331231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
331331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
3314eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3315eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
3316eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
331789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
3318e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3319e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
3320e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
3321e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                final String groupMembershipMimetypeId = Long.toString(mOpenHelper
3322e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
3323ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (mOpenHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
3324e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3325e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3326ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (mOpenHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
3327e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
3328e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
3329e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3330eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3331eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3332eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
33335ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case PRESENCE: {
3334373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setTables(Tables.PRESENCE);
3335373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setProjectionMap(sPresenceProjectionMap);
33365ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
33375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
33385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
33395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case PRESENCE_ID: {
3340373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setTables(Tables.PRESENCE);
3341373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.setProjectionMap(sPresenceProjectionMap);
3342373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov                qb.appendWhere(Presence._ID + "=" + ContentUris.parseId(uri));
33435ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
33445ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
33455ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
3346c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
3347a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
3348c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3349c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
3350c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
3351b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3352b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, contactId, projection);
3353c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3354c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
33551b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
33561b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setTables(mOpenHelper.getContactView());
33571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
33581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
33591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
33601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
33611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setTables(mOpenHelper.getContactView());
33621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
33631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
33641b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
33651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
33661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
33671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setTables(mOpenHelper.getContactView());
33681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
33691b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
33701b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
33711b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
33721b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
33731b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setTables(mOpenHelper.getContactView());
33741b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
33751b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(sContactsInGroupSelect);
33761b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
33771b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
33781b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
33794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
3380f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
3381c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
33824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
33834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
33845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
33855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
33865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
33875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
33885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
33895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
3390038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
3391038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
3392038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
3393038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
33945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
33955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
33964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
33974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
33984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
33994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
34004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
34014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
34025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
34035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
34045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
34055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = lookupContactIdBySourceIds(db, segments);
34075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (contactId == -1) {
34085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
34095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
34105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
34125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
34135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
34155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
34165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
34185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
34195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
34205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
34215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
34225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
34235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
34255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
34265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
34275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
34285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
34295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
34315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
34325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int sourceIdCount = 0;
34335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
34345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
34355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
34365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sourceIdCount++;
34375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
34385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
34395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (sourceIdCount == 0) {
34415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
34425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
34435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
34455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
34465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
34475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
34485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
34495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.sourceIdLookup) {
34505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
34515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
34525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
34535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
34545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
34555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
34565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
34585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
34595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
34605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
34615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
34625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
34635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
34645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
34655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
34665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
34675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
34685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (segment.sourceIdLookup && accountHashCode == segment.accountHashCode
34695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
34705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
34715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
34725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
34735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
34745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
34755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
34765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
34775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
34785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
34805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
34815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupByDisplayNameQuery {
34835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
34845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
34865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
34875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
34885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
34895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
34905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
34915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
34935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
34945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
34955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int NORMALIZED_NAME = 3;
34965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
34975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
34985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
34995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
35005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int displayNameCount = 0;
35015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
35025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
35035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
35045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                displayNameCount++;
35055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
35065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
35075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (displayNameCount == 0) {
35095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return -1;
35105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
35115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // First try sync ids
35135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
35145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
35155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
35165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
35175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (!segment.sourceIdLookup) {
35185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
35195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
35205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
35215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
35225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
35235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
35245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
35255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
35275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
35285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
35295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
35305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
35315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
35325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
35335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
35345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
35355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
35365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
35375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (!segment.sourceIdLookup && accountHashCode == segment.accountHashCode
35385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
35395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
35405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
35415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
35425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
35435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
35445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
35455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
35465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
35475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
35495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
35505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
35525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
35535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
35545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
35555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
35565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
35585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
35595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
35615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
35625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
35635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
35645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
35655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
35665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
35675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
35685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
35695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
35705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
35715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
35725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
35735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
35745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
35755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
35765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
35775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
35785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
35795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
35805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
35815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
35825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
35835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
35845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
35855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
3586ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, String[] projection) {
3587ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        String contactView = mOpenHelper.getContactView();
3588ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        boolean needsPresence = mOpenHelper.isInProjection(projection, Contacts.PRESENCE_STATUS,
3589ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Contacts.PRESENCE_CUSTOM_STATUS);
3590ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        if (!needsPresence) {
3591ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            qb.setTables(contactView);
3592ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            qb.setProjectionMap(sContactsProjectionMap);
3593ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        } else {
3594ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            qb.setTables(contactView + " LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE + " ON ("
3595ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                    + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ") ");
3596ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            qb.setProjectionMap(sContactsWithPresenceProjectionMap);
3597ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
3598ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        }
3599ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
3600ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
36014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
36024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
36034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
36044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (!TextUtils.isEmpty(accountName)) {
36054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
36064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
36074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
36084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
36094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
36104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
36114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
36124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
36134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
3614e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
3615e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
3616e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
3617e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        if (!TextUtils.isEmpty(accountName)) {
3618e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
3619e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
3620e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
3621e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
3622e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
3623e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
3624e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
3625e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
3626e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
3627e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
3628e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
3629e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
3630e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
3631e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
3632e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
36337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
3634c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
3635c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
3636c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
3637c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
3638c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
3639c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private String getLimit(Uri url) {
3640c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limitParam = url.getQueryParameter("limit");
3641c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
3642c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
3643c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
3644c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
3645c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
3646c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
3647c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
3648c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
3649c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
3650c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
3651c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
3652c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
3653c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
3654c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
3655c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
3656c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
3657c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
36585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /**
36595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
36605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
36615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     */
36625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
36635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        int len = cons.length();
36645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
36655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        for (int i = 0; i < len; i++) {
36665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            char c = cons.charAt(i);
36675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
36685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
36695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
36705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
36715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
36725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    || (c == '#') || (c == '*')) {
36735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
36745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
36755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
36765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
36775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
36785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
36795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
36805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
36815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
36825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            return false;
36835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        }
36845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
36855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        return true;
36865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
36875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
368800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
36894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (mOpenHelper.hasRestrictedAccess()) {
369070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
369170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
36926cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            return RawContacts.IS_RESTRICTED + "=0";
369370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
369470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
369570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
369670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
36974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (mOpenHelper.hasRestrictedAccess()) {
369870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
369967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
37005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
37015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
3702619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
3703619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
3704619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
3705b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
3706b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
3707b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
3708b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
3709d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_PHOTO: {
3710b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                if (!"r".equals(mode)) {
3711b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                    throw new FileNotFoundException("Mode " + mode + " not supported.");
3712b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                }
3713b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3714b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
3715b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3716b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                String sql =
3717b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                        "SELECT " + Photo.PHOTO + " FROM " + mOpenHelper.getDataView() +
3718b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                        " WHERE " + Data._ID + "=" + Contacts.PHOTO_ID
3719b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                                + " AND " + RawContacts.CONTACT_ID + "=" + contactId;
3720b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                SQLiteDatabase db = mOpenHelper.getReadableDatabase();
3721b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql, null);
3722d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
3723d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3724d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_LOOKUP:
3725d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
3726d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // TODO: optimize lookup when direct id provided
3727d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final String lookupKey = uri.getPathSegments().get(2);
3728d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3729d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final String selection = RawContacts.CONTACT_ID + "=" + contactId;
3730d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3731d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
3732d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
3733d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
3734d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
3735d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                outputRawContactsAsVCard(localStream, selection, null);
3736d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return buildAssetFileDescriptor(localStream);
3737d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
3738b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3739b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
3740b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov                throw new FileNotFoundException("No file at: " + uri);
3741b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
3742b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
3743b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3744d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
3745d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String VCARD_TYPE_DEFAULT = "default";
3746d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3747d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
3748d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
3749d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
3750d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
3751d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
3752d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        AssetFileDescriptor fd = null;
3753d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
3754d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
3755d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3756d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
3757d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final int size = byteData.length;
3758d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3759d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
3760d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.writeBytes(byteData, 0, 0, size);
3761d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.deactivate();
3762b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3763d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
3764d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
3765d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
3766d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
3767d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        return fd;
3768d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
3769d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3770d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
3771d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
3772d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
3773d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
3774d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
3775d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
3776d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
3777d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
3778d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final VCardComposer composer = new VCardComposer(context, VCARD_TYPE_DEFAULT, false);
3779d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
3780d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3781d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        // TODO: enforce the callers security clause is used
3782d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        if (!composer.init(selection, selectionArgs))
3783d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
3784d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
3785d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
3786d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
3787d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
3788d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
3789d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
3790d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
3791d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
3792b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
3793619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
37947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
37957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
37967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
3797d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static class RawContactsEntityIterator implements EntityIterator {
37987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
37997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
38007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
38017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
38027a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
38037a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
38047a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
38057a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
38067a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
38077a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
38087a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
38097a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
38107a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
38117a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
38127a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
38137a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
38147a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
38157a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
38167a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
38177a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
38187a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
38197a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
38207a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4};
38217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
38227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] PROJECTION = new String[]{
38236cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
38246cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
38256cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.SOURCE_ID,
38266cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.VERSION,
38276cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                RawContacts.DIRTY,
38287a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data._ID,
38297a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.RES_PACKAGE,
38307a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.MIMETYPE,
38317a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA1,
38327a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA2,
38337a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA3,
38347a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA4,
38357a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA5,
38367a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA6,
38377a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA7,
38387a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA8,
38397a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA9,
38407a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA10,
38417a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA11,
38427a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA12,
38437a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA13,
38447a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA14,
38457a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA15,
38467a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC1,
38477a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC2,
38487a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC3,
38497a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.SYNC4,
38507a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.RAW_CONTACT_ID,
38517a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.IS_PRIMARY,
38523cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar                Data.IS_SUPER_PRIMARY,
38537a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Data.DATA_VERSION,
38547a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                GroupMembership.GROUP_SOURCE_ID,
38557a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC1,
38567a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC2,
38577a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                RawContacts.SYNC3,
385894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                RawContacts.SYNC4,
385938446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey                RawContacts.DELETED,
3860c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey                RawContacts.CONTACT_ID,
3861c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey                RawContacts.STARRED};
3862035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
3863035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
3864035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
3865035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
3866035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
3867035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
3868035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
386967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_RES_PACKAGE = 6;
387067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_MIMETYPE = 7;
387167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        private static final int COLUMN_DATA1 = 8;
38727a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_RAW_CONTACT_ID = 27;
38737a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_IS_PRIMARY = 28;
38743cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_IS_SUPER_PRIMARY = 29;
38753cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DATA_VERSION = 30;
38763cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_GROUP_SOURCE_ID = 31;
38773cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC1 = 32;
38783cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC2 = 33;
38793cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC3 = 34;
38803cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_SYNC4 = 35;
38813cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_DELETED = 36;
38823cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_CONTACT_ID = 37;
38833cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar        private static final int COLUMN_STARRED = 38;
38847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3885d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        public RawContactsEntityIterator(ContactsProvider2 provider, String contactsIdString, Uri uri,
38867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
38877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
38887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
38897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final String updatedSortOrder = (sortOrder == null)
38907a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                    ? Data.RAW_CONTACT_ID
38917a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                    : (Data.RAW_CONTACT_ID + "," + sortOrder);
38927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
38937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
38947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
3895226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setTables(Tables.CONTACT_ENTITIES);
38967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
38975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                qb.appendWhere(Data.RAW_CONTACT_ID + "=" + contactsIdString);
38987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
38996cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME);
39006cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE);
3901035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (!TextUtils.isEmpty(accountName)) {
39026cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
3903035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
39046cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                        + RawContacts.ACCOUNT_TYPE + "="
3905035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
3906035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
39077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
39087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    null, null, updatedSortOrder);
39097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
39107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
39117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3912038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
3913038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
3914038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
3915038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
3916038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
3917038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
3918038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana
39197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
39207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
39217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
39227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
39247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
39257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
39267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
39287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
39297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
39307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
39337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
39347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
39367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
39377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
39387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
39407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
39417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
39427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
39447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39457a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            final long rawContactId = c.getLong(COLUMN_RAW_CONTACT_ID);
39467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
39487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
39496cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
39506cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
39515ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            contactValues.put(RawContacts._ID, rawContactId);
39526cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.DIRTY, c.getLong(COLUMN_DIRTY));
39536cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.VERSION, c.getLong(COLUMN_VERSION));
39546cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            contactValues.put(RawContacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
39557a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC1, c.getString(COLUMN_SYNC1));
39567a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC2, c.getString(COLUMN_SYNC2));
39577a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC3, c.getString(COLUMN_SYNC3));
39587a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            contactValues.put(RawContacts.SYNC4, c.getString(COLUMN_SYNC4));
395994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            contactValues.put(RawContacts.DELETED, c.getLong(COLUMN_DELETED));
396038446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey            contactValues.put(RawContacts.CONTACT_ID, c.getLong(COLUMN_CONTACT_ID));
3961c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            contactValues.put(RawContacts.STARRED, c.getLong(COLUMN_STARRED));
39627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
39637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
39647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
39657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
39667a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                if (rawContactId != c.getLong(COLUMN_RAW_CONTACT_ID)) {
39677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
39687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
39697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
39707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
39717a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data._ID, c.getString(COLUMN_DATA_ID));
39727a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
39737a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
39747a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.IS_PRIMARY, c.getString(COLUMN_IS_PRIMARY));
39753cb415e7b97c3e318d7a16aaf7cc5c6b825d349aEvan Millar                dataValues.put(Data.IS_SUPER_PRIMARY, c.getString(COLUMN_IS_SUPER_PRIMARY));
39767a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
39779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
39789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
39799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            c.getString(COLUMN_GROUP_SOURCE_ID));
39809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
39817a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                dataValues.put(Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
39827a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                for (int i = 0; i < DATA_KEYS.length; i++) {
39837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
39847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
39857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
39867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
39877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
39887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
39897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
39907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
39917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
39927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
39937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
39947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
39957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
39967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
39977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
39987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
39997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
40007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
40017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
40027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
40037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4004226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    /**
4005226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
4006226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
4007226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana     */
4008226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    private static class GroupsEntityIterator implements EntityIterator {
4009226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private final Cursor mEntityCursor;
4010226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private volatile boolean mIsClosed;
4011226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4012226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final String[] PROJECTION = new String[]{
4013226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups._ID,
4014226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_NAME,
4015226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.ACCOUNT_TYPE,
4016226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.SOURCE_ID,
4017226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.DIRTY,
4018226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.VERSION,
4019226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.RES_PACKAGE,
4020226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE,
4021226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                Groups.TITLE_RES,
40227a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.GROUP_VISIBLE,
40237a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC1,
40247a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC2,
40257a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC3,
40267a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYNC4,
40277a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                Groups.SYSTEM_ID,
402894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                Groups.NOTES,
402994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                Groups.DELETED};
4030226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4031226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ID = 0;
4032226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_NAME = 1;
4033226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 2;
4034226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_SOURCE_ID = 3;
4035226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_DIRTY = 4;
4036226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_VERSION = 5;
4037226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_RES_PACKAGE = 6;
4038226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE = 7;
4039226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_TITLE_RES = 8;
4040226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        private static final int COLUMN_GROUP_VISIBLE = 9;
40417a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC1 = 10;
40427a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC2 = 11;
40437a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC3 = 12;
40447a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYNC4 = 13;
40457a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_SYSTEM_ID = 14;
40467a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana        private static final int COLUMN_NOTES = 15;
404794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        private static final int COLUMN_DELETED = 16;
4048226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4049226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public GroupsEntityIterator(ContactsProvider2 provider, String groupIdString, Uri uri,
4050226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
4051226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = false;
4052226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4053226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String updatedSortOrder = (sortOrder == null)
4054226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    ? Groups._ID
4055226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    : (Groups._ID + "," + sortOrder);
4056226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4057226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
4058226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
405989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            qb.setTables(provider.mOpenHelper.getGroupView());
4060226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            qb.setProjectionMap(sGroupsProjectionMap);
4061226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (groupIdString != null) {
4062226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups._ID + "=" + groupIdString);
4063226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4064226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String accountName = uri.getQueryParameter(Groups.ACCOUNT_NAME);
4065226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final String accountType = uri.getQueryParameter(Groups.ACCOUNT_TYPE);
4066226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!TextUtils.isEmpty(accountName)) {
4067226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                qb.appendWhere(Groups.ACCOUNT_NAME + "="
4068226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
4069226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + Groups.ACCOUNT_TYPE + "="
4070226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
4071226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4072226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
4073226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    null, null, updatedSortOrder);
4074226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToFirst();
4075226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4076226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4077226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public void close() {
4078226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4079226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("closing when already closed");
4080226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4081226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mIsClosed = true;
4082226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.close();
4083226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4084226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4085226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public boolean hasNext() throws RemoteException {
4086226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4087226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
4088226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4089226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4090226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return !mEntityCursor.isAfterLast();
4091226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4092226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4093038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        public void reset() throws RemoteException {
4094038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            if (mIsClosed) {
4095038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                throw new IllegalStateException("calling reset() when the iterator is closed");
4096038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            }
4097038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            mEntityCursor.moveToFirst();
4098038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
4099e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
4100226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        public Entity next() throws RemoteException {
4101226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (mIsClosed) {
4102226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
4103226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4104226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            if (!hasNext()) {
4105226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
4106226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            }
4107226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4108226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
4109226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4110226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            final long groupId = c.getLong(COLUMN_ID);
4111226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4112226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            // we expect the cursor is already at the row we need to read from
4113226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            ContentValues groupValues = new ContentValues();
4114226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
4115226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
4116226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups._ID, groupId);
4117226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.DIRTY, c.getLong(COLUMN_DIRTY));
4118226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.VERSION, c.getLong(COLUMN_VERSION));
4119226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
4120226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.RES_PACKAGE, c.getString(COLUMN_RES_PACKAGE));
4121226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE, c.getString(COLUMN_TITLE));
4122226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.TITLE_RES, c.getString(COLUMN_TITLE_RES));
4123226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            groupValues.put(Groups.GROUP_VISIBLE, c.getLong(COLUMN_GROUP_VISIBLE));
41247a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC1, c.getString(COLUMN_SYNC1));
41257a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC2, c.getString(COLUMN_SYNC2));
41267a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC3, c.getString(COLUMN_SYNC3));
41277a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYNC4, c.getString(COLUMN_SYNC4));
41287a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.SYSTEM_ID, c.getString(COLUMN_SYSTEM_ID));
412994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            groupValues.put(Groups.DELETED, c.getLong(COLUMN_DELETED));
41307a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana            groupValues.put(Groups.NOTES, c.getString(COLUMN_NOTES));
4131226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            Entity group = new Entity(groupValues);
4132226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4133226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            mEntityCursor.moveToNext();
4134226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4135226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            return group;
4136226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana        }
4137226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana    }
4138226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4139a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
41407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
41417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
4142568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
4143568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
41447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
41457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
41465ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS:
41475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID:
41487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
41495ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                if (match == RAW_CONTACTS_ID) {
41507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
41517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
41527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
4153d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return new RawContactsEntityIterator(this, contactsIdString,
41547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        uri, selection, selectionArgs, sortOrder);
4155226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS:
4156226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana            case GROUPS_ID:
4157226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                String idString = null;
4158226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                if (match == GROUPS_ID) {
4159226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                    idString = uri.getPathSegments().get(1);
4160226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                }
4161226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana
4162226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                return new GroupsEntityIterator(this, idString,
4163226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                        uri, selection, selectionArgs, sortOrder);
41647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
41657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
41667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
41677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
41687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
41694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
41704f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
4171a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
41724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
4173b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
4174b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP:
4175be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
4176b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
4177b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
4178b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
4179b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
4180be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
4181b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
4182b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
4183508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
4184b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return mOpenHelper.getDataMimeType(ContentUris.parseId(uri));
418548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
418648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
418748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
418848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
418948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
419048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
419148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
419248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
419348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
419448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
419548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
419648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
4197b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
4198b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
4199b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
4200b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
4201b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
4202b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
4203b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
4204b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
4205c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
4206c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
4207c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
4208c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
420961efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
421061efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
42114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
42124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
42137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
421425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private void setDisplayName(long rawContactId, String displayName, int bestDisplayNameSource) {
42153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (displayName != null) {
421625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindString(1, displayName);
42173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        } else {
421825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            mRawContactDisplayNameUpdate.bindNull(1);
42193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
422025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(2, bestDisplayNameSource);
422125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(3, rawContactId);
422225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
42233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
42243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
422573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
422673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
422773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
422873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
422973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate.bindLong(1, rawContactId);
423073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        mRawContactDirtyUpdate.execute();
423173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
423273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
4233c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4234c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
4235c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
4236c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4237c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4238c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4239653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
4240c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
4241653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
4242653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
4243c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
4244c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4245c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
4246c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
4247c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
4248c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
4249c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
4250c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
4251c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
4252653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
4253c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
4254653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
4255653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
4256c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
4257c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
4258ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4259f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForEmail(long rawContactId, long dataId, String email) {
4260f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
4261f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4262f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4263f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4264f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
4265f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (tokens.length == 0) {
4266f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4267f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4268f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4269f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String address = tokens[0].getAddress();
4270f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int at = address.indexOf('@');
4271f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (at != -1) {
4272f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            address = address.substring(0, at);
4273f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4274f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4275f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4276f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
4277f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4278f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4279f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4280f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
4281f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4282f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
4283f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
4284f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
4285f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4286f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4287f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
4288f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
4289f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4290f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4291f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4292f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name) {
4293f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name);
4294f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4295f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4296f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4297f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Returns nickname cluster IDs or null. Maintains cache.
4298f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4299f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] getCommonNicknameClusters(String normalizedName) {
4300f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        SoftReference<String[]> ref;
4301f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
4302f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        synchronized (mNicknameClusterCache) {
4303f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (mNicknameClusterCache.containsKey(normalizedName)) {
4304f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                ref = mNicknameClusterCache.get(normalizedName);
4305f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                if (ref == null) {
4306f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    return null;
4307f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
4308f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = ref.get();
4309f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4310f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4311f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4312f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (clusters == null) {
4313f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            clusters = loadNicknameClusters(normalizedName);
4314f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ref = clusters == null ? null : new SoftReference<String[]>(clusters);
4315f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            synchronized (mNicknameClusterCache) {
4316f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                mNicknameClusterCache.put(normalizedName, ref);
4317f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4318f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4319f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
4320f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4321f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4322f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String[] loadNicknameClusters(String normalizedName) {
4323f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
4324f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String[] clusters = null;
4325f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        Cursor cursor = db.query(NicknameLookupQuery.TABLE, NicknameLookupQuery.COLUMNS,
4326f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
4327f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                null, null, null);
4328f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        try {
4329f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            int count = cursor.getCount();
4330f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (count > 0) {
4331f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                clusters = new String[count];
4332f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                for (int i = 0; i < count; i++) {
4333f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    cursor.moveToNext();
4334f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    clusters[i] = cursor.getString(NicknameLookupQuery.CLUSTER);
4335f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
4336f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
4337f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        } finally {
4338f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            cursor.close();
4339f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4340f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return clusters;
4341f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4342f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4343f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
4344f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4345f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
4346f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
4347f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4348f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4349f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4350f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
4351f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
4352f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
4353f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4354f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4355f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
4356f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
4357f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return ContactsProvider2.this.getCommonNicknameClusters(normalizedName);
4358f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
4359f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4360f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4361f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4362f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
4363f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4364f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
4365f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, rawContactId);
4366f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, dataId);
4367f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, lookupType);
4368f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 4, name);
4369f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
4370f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4371f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4372f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
4373f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
4374f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
4375f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
4376f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupDelete, 1, dataId);
4377f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
4378f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
4379f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
43802d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
4381e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append("(SELECT DISTINCT " + RawContacts.CONTACT_ID + " FROM " + Tables.RAW_CONTACTS
4382e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                + " JOIN name_lookup ON(" + RawContactsColumns.CONCRETE_ID + "=raw_contact_id)"
4383e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                + " WHERE normalized_name GLOB '");
4384e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
43853de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov        sb.append("*' AND ("
43863de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
43873de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov                + " OR "
43883de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NICKNAME + "))");
4389e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
4390e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
43915ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
4392c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
4393c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam, null);
4394c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
4395c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
4396c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4397a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam,
4398c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            String limit) {
43995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), limit);
44005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
44015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
44025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
44035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            String limit) {
4404c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append("(SELECT DISTINCT raw_contact_id FROM name_lookup WHERE normalized_name GLOB '");
44055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
44063de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov        sb.append("*' AND ("
44073de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
44083de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov                + " OR "
44093de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NICKNAME + ")");
44103de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
4411c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limit != null) {
4412c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            sb.append(" LIMIT ").append(limit);
4413c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
4414c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        sb.append(")");
4415ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
4416ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
44174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
44184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
44194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
44204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
4421b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
4422b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
4423b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
4424b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
4425b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
44264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
44274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
4428b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
4429b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
4430b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
4431caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
4432caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
4433caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
4434caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
4435df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
4436df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
4437caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
4438caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
4439caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
4440caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
44416f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
4442caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
44436f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
4444caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
44454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
4446