ContactsProvider2.java revision e8d2c8276d6331843410c97751e46fc50b257379
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;
21b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
22b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
23b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
24b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkeyimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
26b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
27b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
28b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
29b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
30b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
31b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
32b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
33b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
34b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
35b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
36b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
37b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
38a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Lists;
39a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Maps;
40a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport com.google.android.collect.Sets;
413de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikov
42b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
43caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
445b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
45bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.Notification;
46bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.NotificationManager;
47bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.PendingIntent;
48c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
49568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
50568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
516ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
5235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
5367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
5467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
55627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
56bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.Intent;
57568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
59627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
6067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
613de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.content.SharedPreferences.Editor;
62b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.content.res.AssetFileDescriptor;
634cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Taoimport android.content.res.Configuration;
641129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikovimport android.database.CharArrayBuffer;
654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
66ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
67ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
6809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
6909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
70a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
71b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.database.sqlite.SQLiteContentHelper;
724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
74c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
7651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikovimport android.os.AsyncTask;
776ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
78d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.os.MemoryFile;
79b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
800e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
81d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.pim.vcard.VCardComposer;
827a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawaimport android.pim.vcard.VCardConfig;
833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
84508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
853de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
863de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.LiveFolders;
873de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.OpenableColumns;
883de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.SyncStateContract;
89b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
90ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
913de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
923de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.DisplayNameSources;
945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.FullNameStyle;
953de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
96bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
973de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.PhoneticNameStyle;
9909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1003de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
101916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1023de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
10382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
1043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
105ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
106ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
1073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
1083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
1093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
110de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
111b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
1124097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
11367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
114a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
115a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
116c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
118d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
119b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
120d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
121d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
12242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
12542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
126b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1270e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
129622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
130b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1310e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
132ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1385b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
139caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
140bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
141bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
142bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
144619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
145619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
146619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1503d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
151b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1533d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1543d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
155b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
156b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
15751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
1583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1590e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1600e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
161a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1635e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final String TIMES_CONTACED_SORT_COLUMN = "times_contacted_sort";
1645e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
165d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
1665e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            + TIMES_CONTACED_SORT_COLUMN + " DESC, "
1679b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
168d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
169d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
170d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
171d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1726e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
1739b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
1749b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1759b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
1769b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
1776e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
1789b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
1799b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1809b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
1819b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
182de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
183de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
184d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
185d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
1885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_DATA = 1004;
1895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
1935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_PHOTO = 1009;
194f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
19542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private static final int CONTACTS_AS_MULTI_VCARD = 1011;
1964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
1985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
1995ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
20046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
2014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2026bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2036bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
204ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
20548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
20648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
20748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
20848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
20948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
21048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
21148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
21248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
213a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2146bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2156bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
216b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
217b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
218b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
21982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
22082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
22231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
22331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
224eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
225eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
226ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
227ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
228ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
229ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
23035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
231b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
23235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
233c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
234c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
235c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
24146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
24246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
24309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
24409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
245d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
246f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
247f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
248f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
24967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
25067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2516cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
253f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
254ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
255ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
256d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
25767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
258d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
259ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
26114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
26267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
26488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
2653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
2683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
269f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
27088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
27188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
27288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
27388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
27488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
27588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
27688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
277f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
2783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
28014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
2813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
2833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
284f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
2853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
28714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
288321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
28920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
29020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
291321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
292321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
29320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
29420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
295f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
29619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private interface RawContactsQuery {
29719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
29819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
29919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
300ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
301ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
302ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
30319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
30419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
30519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
306ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
307ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
30819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
30919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
310c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
311df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
312caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
31371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
31471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
31571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
31671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
31771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
31871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
31971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
32071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
32171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
32271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
32371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
32471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
32571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
32671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
327a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
328a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
329a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
330a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
331a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
332a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
333a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
334a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
335a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
336a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
337a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
338a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
339916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
340916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
341916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
342916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
343916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NICKNAME + "," +
344916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_SHORTHAND + "," +
345f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.ORGANIZATION + "," +
346f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.NAME_CONSONANTS;
347916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
348916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
349038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
350038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    private static final HashMap<String, String> sCountProjectionMap;
351e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
3524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
353916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
354916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final HashMap<String, String> sContactsProjectionWithSnippetMap;
355916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
3565e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
3575e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentStarredProjectionMap;
3585e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentFrequentProjectionMap;
359f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
360f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final HashMap<String, String> sContactsVCardProjectionMap;
361ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
362d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sRawContactsProjectionMap;
36346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    /** Contains the columns from the raw contacts entity view*/
36446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final HashMap<String, String> sRawContactsEntityProjectionMap;
3654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
3664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private static final HashMap<String, String> sDataProjectionMap;
3675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
3685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private static final HashMap<String, String> sDistinctDataProjectionMap;
3699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
370e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    private static final HashMap<String, String> sPhoneLookupProjectionMap;
371ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
372ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
373ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
374ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
375373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
376b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
377eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
378eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final HashMap<String, String> sSettingsProjectionMap;
37982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
38082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final HashMap<String, String> sStatusUpdatesProjectionMap;
3811b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
3821b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final HashMap<String, String> sLiveFoldersProjectionMap;
3837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
3859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
3869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
3879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
3889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
3899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
3902526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
3912526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
392bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
393bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
394bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
395bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
39651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
397c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
398c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
3993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
400c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
4013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
40225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
40382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Precompiled sql statement for updating an aggregated status update */
404a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mLastStatusUpdate;
405f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
406f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupDelete;
407a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateAutoTimestamp;
408a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateInsert;
409a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateReplace;
4100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private SQLiteStatement mStatusAttributionUpdate;
411a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateDelete;
412f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov    private SQLiteStatement mResetNameVerifiedForOtherRawContacts;
413a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
414f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdEmail;
415f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdIm;
4161129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdStructuredName;
4171129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdOrganization;
4181129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdNickname;
4191129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdPhone;
420f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
4211129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
4221129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
4232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
4242526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
425f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
426f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
4274f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
4284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
429a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
430d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
431d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
432d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
4333653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
4343653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
4352d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
4362d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
4373653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO);
4385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
4395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
4405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
441f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
44242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
44342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
4445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
445ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
446ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
4475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
4483653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4495ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
4505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
4515ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
45246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
45346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
45446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
455b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
4564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
4574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
458ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
45948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
4605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
461ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
4624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
46348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
4645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
4655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
4664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
467ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
46848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
4691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
470ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
471ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
472ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
473ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
47435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
475b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
476b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
47735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
478a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
479b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
480b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
481b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
482b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
4834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
484eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
485eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
48682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
48782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
4881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
489c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
490c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
491c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
492c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
4932d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
494c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
495c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4961b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
4971b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
4981b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
4991b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
5001b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
5011b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
5021b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
5031b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
50409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
50509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
50619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
50719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
50819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    static {
509038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap = new HashMap<String, String>();
510038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
511e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
5124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap = new HashMap<String, String>();
5134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts._ID, Contacts._ID);
5145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME_PRIMARY);
5155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME_ALTERNATIVE,
5165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE);
5175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME_SOURCE, Contacts.DISPLAY_NAME_SOURCE);
5185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHONETIC_NAME, Contacts.PHONETIC_NAME);
5195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHONETIC_NAME_STYLE, Contacts.PHONETIC_NAME_STYLE);
5205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SORT_KEY_PRIMARY, Contacts.SORT_KEY_PRIMARY);
5215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SORT_KEY_ALTERNATIVE, Contacts.SORT_KEY_ALTERNATIVE);
5224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
5234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
5244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
5254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
5264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
5274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
528f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        sContactsProjectionMap.put(Contacts.HAS_PHONE_NUMBER, Contacts.HAS_PHONE_NUMBER);
5294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
5305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sContactsProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
531f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
5323296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
5333296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_PRESENCE,
5343296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
5353296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS,
5363296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
5373296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
5383296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
5393296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
5403296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
5413296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_LABEL,
5423296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
5433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_ICON,
5443296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
5453296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
546916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sContactsProjectionWithSnippetMap = new HashMap<String, String>();
547916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sContactsProjectionWithSnippetMap.putAll(sContactsProjectionMap);
548916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_MIMETYPE,
549916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_MIMETYPE);
550916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_DATA_ID,
551916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_DATA_ID);
5529c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_DATA1,
5539c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_DATA1);
5549c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_DATA2,
5559c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_DATA2);
5569c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_DATA3,
5579c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_DATA3);
5589c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_DATA4,
5599c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_DATA4);
560916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5615e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
5625e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
5635e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Long.MAX_VALUE + " AS " + TIMES_CONTACED_SORT_COLUMN);
5645e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5655e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
5665e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
5675e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Contacts.TIMES_CONTACTED + " AS " + TIMES_CONTACED_SORT_COLUMN);
5685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
569f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap = Maps.newHashMap();
570f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME
571d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                + " || '.vcf' AS " + OpenableColumns.DISPLAY_NAME);
572ba355248c255551bc65d8023b968513cbe9bcdf3Jeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.SIZE, "NULL AS " + OpenableColumns.SIZE);
5734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap = new HashMap<String, String>();
5754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
5764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
5774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
5824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DELETED, RawContacts.DELETED);
5835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DISPLAY_NAME_PRIMARY,
5845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.DISPLAY_NAME_PRIMARY);
5855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DISPLAY_NAME_ALTERNATIVE,
5865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.DISPLAY_NAME_ALTERNATIVE);
5875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DISPLAY_NAME_SOURCE,
5885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.DISPLAY_NAME_SOURCE);
5895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.PHONETIC_NAME,
5905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.PHONETIC_NAME);
5915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.PHONETIC_NAME_STYLE,
5925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.PHONETIC_NAME_STYLE);
593f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.NAME_VERIFIED,
594f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                RawContacts.NAME_VERIFIED);
5955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SORT_KEY_PRIMARY,
5965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.SORT_KEY_PRIMARY);
5975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SORT_KEY_ALTERNATIVE,
5985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.SORT_KEY_ALTERNATIVE);
5994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.TIMES_CONTACTED, RawContacts.TIMES_CONTACTED);
6004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.LAST_TIME_CONTACTED,
6014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.LAST_TIME_CONTACTED);
6024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CUSTOM_RINGTONE, RawContacts.CUSTOM_RINGTONE);
6034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SEND_TO_VOICEMAIL, RawContacts.SEND_TO_VOICEMAIL);
6044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.STARRED, RawContacts.STARRED);
6054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
6064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC1, RawContacts.SYNC1);
6074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC2, RawContacts.SYNC2);
6084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC3, RawContacts.SYNC3);
6094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC4, RawContacts.SYNC4);
6102815f58f72f109790585931f601a63ddc02536a5Evan Millar
6114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap = new HashMap<String, String>();
6124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data._ID, Data._ID);
6134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
6144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
6154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
6164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
6174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
6184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
6194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA1, Data.DATA1);
6204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA2, Data.DATA2);
6214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA3, Data.DATA3);
6224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA4, Data.DATA4);
6234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA5, Data.DATA5);
6244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA6, Data.DATA6);
6254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA7, Data.DATA7);
6264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA8, Data.DATA8);
6274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA9, Data.DATA9);
6284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA10, Data.DATA10);
6294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA11, Data.DATA11);
6304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA12, Data.DATA12);
6314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA13, Data.DATA13);
6324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA14, Data.DATA14);
6334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA15, Data.DATA15);
6344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
6354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
6364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
6374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
63882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sDataProjectionMap.put(Data.CONTACT_ID, Data.CONTACT_ID);
6394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
6404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
6414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
6424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
6434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
644f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.NAME_VERIFIED, RawContacts.NAME_VERIFIED);
64556d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
6464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
6475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME_ALTERNATIVE,
6485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE);
6495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME_SOURCE, Contacts.DISPLAY_NAME_SOURCE);
6505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHONETIC_NAME, Contacts.PHONETIC_NAME);
6515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHONETIC_NAME_STYLE, Contacts.PHONETIC_NAME_STYLE);
6525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SORT_KEY_PRIMARY, Contacts.SORT_KEY_PRIMARY);
6535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SORT_KEY_ALTERNATIVE, Contacts.SORT_KEY_ALTERNATIVE);
6544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
6554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
6564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
6574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
6584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
6594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
660a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
6614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
662a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
66346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        HashMap<String, String> columns;
66446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns = new HashMap<String, String>();
66546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts._ID, RawContacts._ID);
66646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
66746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
66846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
66946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
67046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.VERSION, RawContacts.VERSION);
67146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DIRTY, RawContacts.DIRTY);
67246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DELETED, RawContacts.DELETED);
673bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey        columns.put(RawContacts.IS_RESTRICTED, RawContacts.IS_RESTRICTED);
67446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC1, RawContacts.SYNC1);
67546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC2, RawContacts.SYNC2);
67646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC3, RawContacts.SYNC3);
67746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC4, RawContacts.SYNC4);
678f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        columns.put(RawContacts.NAME_VERIFIED, RawContacts.NAME_VERIFIED);
67946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
68046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.MIMETYPE, Data.MIMETYPE);
68146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA1, Data.DATA1);
68246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA2, Data.DATA2);
68346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA3, Data.DATA3);
68446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA4, Data.DATA4);
68546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA5, Data.DATA5);
68646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA6, Data.DATA6);
68746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA7, Data.DATA7);
68846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA8, Data.DATA8);
68946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA9, Data.DATA9);
69046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA10, Data.DATA10);
69146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA11, Data.DATA11);
69246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA12, Data.DATA12);
69346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA13, Data.DATA13);
69446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA14, Data.DATA14);
69546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA15, Data.DATA15);
69646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC1, Data.SYNC1);
69746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC2, Data.SYNC2);
69846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC3, Data.SYNC3);
69946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC4, Data.SYNC4);
70046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.Entity.DATA_ID, RawContacts.Entity.DATA_ID);
70146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.STARRED, Data.STARRED);
70246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
70346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
70446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
70546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
70646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        sRawContactsEntityProjectionMap = columns;
70746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
7083296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
7093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_PRESENCE,
7103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
7113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS,
7123296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
7133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
7143296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7153296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
7163296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7173296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
7183296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7193296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
7203296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
7213296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
7223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
7233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.PRESENCE,
7243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
7253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS,
7263296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
7273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_TIMESTAMP,
7283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7293296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_RES_PACKAGE,
7303296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7313296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_LABEL,
7323296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7333296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_ICON,
7343296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
7353296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
7365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        // Projection map for data grouped by contact (not raw contact) and some data field(s)
7375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap = new HashMap<String, String>();
7385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data._ID,
7395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                "MIN(" + Data._ID + ") AS " + Data._ID);
7405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
7415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
7425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
7435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
7445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
7455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA1, Data.DATA1);
7465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA2, Data.DATA2);
7475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA3, Data.DATA3);
7485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA4, Data.DATA4);
7495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA5, Data.DATA5);
7505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA6, Data.DATA6);
7515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA7, Data.DATA7);
7525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA8, Data.DATA8);
7535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA9, Data.DATA9);
7545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA10, Data.DATA10);
7555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA11, Data.DATA11);
7565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA12, Data.DATA12);
7575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA13, Data.DATA13);
7585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA14, Data.DATA14);
7595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA15, Data.DATA15);
7605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
7615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
7625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
7635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
7645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
7658f1631f8a610e7278526916ce73ac1e422a5c9b8Jeff Sharkey        sDistinctDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
7665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
7675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME_ALTERNATIVE,
7685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE);
7695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME_SOURCE, Contacts.DISPLAY_NAME_SOURCE);
7705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHONETIC_NAME, Contacts.PHONETIC_NAME);
7715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHONETIC_NAME_STYLE, Contacts.PHONETIC_NAME_STYLE);
7725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SORT_KEY_PRIMARY, Contacts.SORT_KEY_PRIMARY);
7735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SORT_KEY_ALTERNATIVE,
7745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Contacts.SORT_KEY_ALTERNATIVE);
7755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
7765e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
7775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
7785e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
7795e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
7805e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
781a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
7825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID,
7835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID);
7845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
7853296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
7863296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_PRESENCE,
7873296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
7883296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS,
7893296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
7903296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
7913296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7923296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
7933296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7943296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
7953296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7963296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
7973296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
7983296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
7993296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
8003296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.PRESENCE,
8013296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
8023296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS,
8033296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
8043296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_TIMESTAMP,
8053296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
8063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_RES_PACKAGE,
8073296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
8083296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_LABEL,
8093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
8103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_ICON,
8113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
8123296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
813e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap = new HashMap<String, String>();
814e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
815fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts._ID
816fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup._ID);
81756d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sPhoneLookupProjectionMap.put(PhoneLookup.LOOKUP_KEY,
818fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.LOOKUP_KEY
819fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.LOOKUP_KEY);
820e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
821fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.DISPLAY_NAME
822fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.DISPLAY_NAME);
823e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
824fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.LAST_TIME_CONTACTED
825e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
826e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
827fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.TIMES_CONTACTED
828fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.TIMES_CONTACTED);
829e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
830fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.STARRED
831fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.STARRED);
832e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
833fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.IN_VISIBLE_GROUP
834fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
835e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
836fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.PHOTO_ID
837fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.PHOTO_ID);
838e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
839fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.CUSTOM_RINGTONE
840fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.CUSTOM_RINGTONE);
841e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
842fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.HAS_PHONE_NUMBER
843fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
844e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
845fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.SEND_TO_VOICEMAIL
846e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
847e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
848e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
849e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
850e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.TYPE + " AS " + PhoneLookup.TYPE);
851e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
852e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.LABEL + " AS " + PhoneLookup.LABEL);
8539261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
854ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
855ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
85689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups._ID, Groups._ID);
857035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
858035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
8599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
8609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
8619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
86289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.RES_PACKAGE, Groups.RES_PACKAGE);
863ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
86467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
865ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
8663cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
86794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        columns.put(Groups.DELETED, Groups.DELETED);
8683cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
86938446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey        columns.put(Groups.SHOULD_SYNC, Groups.SHOULD_SYNC);
87089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC1, Groups.SYNC1);
87189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC2, Groups.SYNC2);
87289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC3, Groups.SYNC3);
87389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC4, Groups.SYNC4);
874ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
875ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
8766cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts and groups projection map
877ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
878ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
879d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
880d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
881ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
882ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
883ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
884d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
885d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
886ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
887f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov                + " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
888ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
889ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
890b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
891b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
892b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
893b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
8940c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID1);
8950c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID2, AggregationExceptions.RAW_CONTACT_ID2);
896b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
897b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
898eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        // Settings projection map
899eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns = new HashMap<String, String>();
900eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
901eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
902eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
903eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
904341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey        columns.put(Settings.ANY_UNSYNCED, "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
905341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + ",(SELECT (CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL THEN 1 ELSE MIN("
906341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Groups.SHOULD_SYNC + ") END) FROM " + Tables.GROUPS + " WHERE "
907fc4e892529eccdfa42121f0304ec7d0dbb42d6c9Dmitri Plotnikov                + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME
908341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
909341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0 THEN 1 ELSE 0 END) AS "
910341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Settings.ANY_UNSYNCED);
91168936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
91268936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " GROUP BY "
91368936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID + " HAVING " + Clauses.HAVING_NO_GROUPS
91468936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + ")) AS " + Settings.UNGROUPED_COUNT);
91568936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
916e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
91768936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Contacts.HAS_PHONE_NUMBER + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
91868936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + " HAVING " + Clauses.HAVING_NO_GROUPS + ")) AS "
91968936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Settings.UNGROUPED_WITH_PHONES);
920eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        sSettingsProjectionMap = columns;
921eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
922373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns = new HashMap<String, String>();
9234dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(PresenceColumns.RAW_CONTACT_ID, PresenceColumns.RAW_CONTACT_ID);
9240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.DATA_ID,
9250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DataColumns.CONCRETE_ID + " AS " + StatusUpdates.DATA_ID);
92682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_ACCOUNT, StatusUpdates.IM_ACCOUNT);
92782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_HANDLE, StatusUpdates.IM_HANDLE);
92882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PROTOCOL, StatusUpdates.PROTOCOL);
92970c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // We cannot allow a null in the custom protocol field, because SQLite3 does not
93070c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // properly enforce uniqueness of null values
93182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.CUSTOM_PROTOCOL, "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL
93282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + "='' THEN NULL ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END) AS "
93382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + StatusUpdates.CUSTOM_PROTOCOL);
93482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PRESENCE, StatusUpdates.PRESENCE);
9350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS, StatusUpdates.STATUS);
9360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_TIMESTAMP, StatusUpdates.STATUS_TIMESTAMP);
9370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_RES_PACKAGE, StatusUpdates.STATUS_RES_PACKAGE);
9380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_ICON, StatusUpdates.STATUS_ICON);
9390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_LABEL, StatusUpdates.STATUS_LABEL);
94082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sStatusUpdatesProjectionMap = columns;
94119a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
9421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // Live folder projection
9431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap = new HashMap<String, String>();
9441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders._ID,
9451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts._ID + " AS " + LiveFolders._ID);
9461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders.NAME,
9471b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts.DISPLAY_NAME + " AS " + LiveFolders.NAME);
9481b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // TODO: Put contact photo back when we have a way to display a default icon
9491b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // for contacts without a photo
9501b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP,
9511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        //      Photos.DATA + " AS " + LiveFolders.ICON_BITMAP);
9524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
9534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
9543296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    private static void addProjection(HashMap<String, String> map, String toField, String fromField) {
9553296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        map.put(toField, fromField + " AS " + toField);
9563296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    }
9573296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
9583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
9593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
9603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
9613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
9623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
964653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
9653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9661129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        @SuppressWarnings("all")
9673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
9683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
969a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
970a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
971a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
972a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
973a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
974a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
975a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
976a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
9773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
979653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
980653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
981b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
982653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
983653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
984653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
985653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
9863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
9873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
9883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
9895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
990e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
991e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
992e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
993e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
994653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
995e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
996e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
997e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
9983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
10013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
10023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
1003813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         *
1004813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * @return true if update changed something
10053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1006813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1007f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
100814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
100914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1010653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1011653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
1012653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
1013653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
1014653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
1015653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1016653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
1017653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
1018653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
1019653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
1020653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
1021653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1022653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
1023653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
1024653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1025653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1026653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
10274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
10284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " =?", mSelectionArgs1);
1029653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1030653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1031f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
1032653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
1033653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1034813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1035813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
10363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
103914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
104014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
104114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
10424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
10434da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=?", mSelectionArgs1);
10444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
10454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
10463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
10475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
10483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
10503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10525ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
10534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            long mimeTypeId = getMimeTypeId();
1054e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
1055e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
10564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
10574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            Cursor c = db.query(DataDeleteQuery.TABLE,
10584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    DataDeleteQuery.CONCRETE_COLUMNS,
10594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=?" +
10604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        " AND " + DataColumns.MIMETYPE_ID + "=" + mimeTypeId,
10614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
10623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
1063e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
106414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
1065f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
1066e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
1067e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
1068e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
1069e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
10703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
10713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
10723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
10733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            if (primaryId != -1) {
10754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                setIsPrimary(rawContactId, primaryId, mimeTypeId);
10764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            }
1077e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1078e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1079e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
1080e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
1081e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
1082e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
1083e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1084e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
10853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
108725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
1088285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1089d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                updateRawContactDisplayName(db, rawContactId);
1090fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(db, rawContactId);
1091285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
10923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1093a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1094622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1095622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
1096813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * as baseline, but augmented with any updates.  Returns null if there is
1097813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * no change.
1098622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1099622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
1100622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
1101813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            boolean changing = false;
1102622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
11034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
11044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=?",
11054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
1106622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
1107622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
1108622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
1109622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
1110813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        final String value = cursor.getString(i);
1111813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        if (!changing && update.containsKey(key)) {
1112813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            Object newValue = update.get(key);
1113813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            String newString = newValue == null ? null : newValue.toString();
1114813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            changing |= !TextUtils.equals(newString, value);
1115813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        }
1116813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        values.put(key, value);
1117622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
1118622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
1119622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
1120622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1121622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1122813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!changing) {
1123813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return null;
1124813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1125813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1126622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1127622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1128622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
11293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
11323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
11343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
11353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
1139622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
11403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1141622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
11423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1143622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
11443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
11475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1148622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
114914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
115014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
115114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1152f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1153d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            Integer fullNameStyle = values.getAsInteger(StructuredName.FULL_NAME_STYLE);
1154d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name,
115551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                    fullNameStyle != null
115651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            ? mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)
115751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            : FullNameStyle.UNDEFINED);
115848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId, values);
115925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1160813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
116114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
116214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
116314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
116414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1165813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1166f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1167622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1168622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1169cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1170622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1171813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {  // No change
1172813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1173813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1174813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1175622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
117614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1177f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1178f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME)) {
1179f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name = values.getAsString(StructuredName.DISPLAY_NAME);
1180f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
1181d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                Integer fullNameStyle = values.getAsInteger(StructuredName.FULL_NAME_STYLE);
1182d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name,
118351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                        fullNameStyle != null
118451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                ? mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)
118551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                : FullNameStyle.UNDEFINED);
118648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                insertNameLookupForPhoneticName(rawContactId, dataId, values);
118714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
118825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1189813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
1190813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
119114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
119214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
119314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
119414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
119514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
119614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
119714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
119814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
119914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1200f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
120125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1202813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
120314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
12043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1207622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
12083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1209622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1210622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1211622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1212622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
12133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1214622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1215622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1216622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1217622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1218622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1219622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1220622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
122167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1222622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
122367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
122467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1225622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1226622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
12278c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1228622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1229622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
123067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
123167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
123267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
123367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
123467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
123567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
12368c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1237622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
12384cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                // As the name could be changed, let's guess the name style again.
12394cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                name.fullNameStyle = FullNameStyle.UNDEFINED;
12404cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                mSplitter.guessNameStyle(name);
12415dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
12425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                final String joined = mSplitter.join(name, true);
1243622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
12445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
12455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                update.put(StructuredName.FULL_NAME_STYLE, name.fullNameStyle);
12465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
12474cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao            } else if (touchedUnstruct && touchedStruct){
1248d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.FULL_NAME_STYLE)) {
1249d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.FULL_NAME_STYLE,
1250d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessFullNameStyle(unstruct));
12514cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1252d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.PHONETIC_NAME_STYLE)) {
1253d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.PHONETIC_NAME_STYLE,
1254d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessPhoneticNameStyle(unstruct));
12554cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1256622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1257622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1258622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1259622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1260622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1261622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1262622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1263622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1264622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1265622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1266622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1267622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1268622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1269622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1270622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1271622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1272622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1273622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1274622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1275813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1276f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1277622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1278622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1279813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {    // No change
1280813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1281813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1282813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1283622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1284f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1285813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1286622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1287622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1288622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1289622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1290622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1291622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1292622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1293622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1294622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1295622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1296622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1297622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1298622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1299622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1300622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1301622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1302622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1303622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
130467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
130567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
130667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
130767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1308622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1309622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1310622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1311622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1312622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1313622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
131467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
131567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
131667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1317622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1318622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1319622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
13203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
13213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
13253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
13273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
13283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
13303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
13313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
13323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
13333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13365ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1337622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1338622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1339622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
13403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1341622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1342813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1343f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1344622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1345622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1346813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {        // No change
1347813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1348813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1349622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1350813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return super.update(db, values, c, callerIsSyncAdapter);
1351622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
13523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1353622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1354622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1355622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1356622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1357622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1358622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1359622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1360622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
13613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1362622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1363622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1364622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1365622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1366622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
13673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
13713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
13733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
13743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13775ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1378a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1379a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1380a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1381a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1382a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
138325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1384a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1385a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
13863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
1389813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1390f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1391813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1392813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1393813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
139414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
139531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsCompany = values.containsKey(Organization.COMPANY);
139631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsTitle = values.containsKey(Organization.TITLE);
139731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            if (containsCompany || containsTitle) {
1398813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1399813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1400813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
140131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String company;
140231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
140331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsCompany) {
140431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = values.getAsString(Organization.COMPANY);
140531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
140631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
140731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = DatabaseUtils.stringForQuery(db,
140831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.COMPANY +
140931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
141031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
141131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
141231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
141331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String title;
141431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsTitle) {
141531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = values.getAsString(Organization.TITLE);
141631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
141731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
141831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = DatabaseUtils.stringForQuery(db,
141931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.TITLE +
142031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
142131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
142231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
142331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
142431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                deleteNameLookup(dataId);
142531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                insertNameLookupForOrganization(rawContactId, dataId, company, title);
142631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
142731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
142831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            }
1429813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
143014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
143114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
143214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
143314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1434a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
143514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
143614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
143714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
143825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1439a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
144014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
144114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
144214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
144314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
14443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
14453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
14463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
14473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
14483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
14493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
14503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
14513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
14533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1454e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1455e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1456e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1457e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1458e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1459e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1460e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
14615ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1462813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String email = values.getAsString(Email.DATA);
146314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
146414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
146514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
146625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1467813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String address = insertNameLookupForEmail(rawContactId, dataId, email);
1468813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (address != null) {
1469813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1470813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
147114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
147214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
147314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
147414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1475813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1476f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1477813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1478813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1479813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
148014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1481b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Email.DATA)) {
1482813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1483813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1484813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1485b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String address = values.getAsString(Email.DATA);
1486b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                deleteNameLookup(dataId);
1487b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                insertNameLookupForEmail(rawContactId, dataId, address);
1488b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1489813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1490b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1491813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1492813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
149314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
149414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
149514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
149614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
149714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
149814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
149914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
150014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
150114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1502f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
150325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1504813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
150514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1506e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1507e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1508e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1509e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1510e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1511e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1512e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1513e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1514e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1515e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1516e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1517e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1518e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1519e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
152014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
152114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
152214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
152314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
152414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
152514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
152614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
152714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
152814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
152914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
153014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
153114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1532813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!TextUtils.isEmpty(nickname)) {
1533813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1534813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                insertNameLookupForNickname(rawContactId, dataId, nickname);
1535813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1536813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
153714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
153814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
153914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
154014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1541813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1542f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
154314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
154414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
154514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1546813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1547813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1548813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
154914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1550b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Nickname.NAME)) {
1551b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String nickname = values.getAsString(Nickname.NAME);
1552b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                deleteNameLookup(dataId);
1553b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                insertNameLookupForNickname(rawContactId, dataId, nickname);
1554b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1555813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1556b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1557813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1558813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
155914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
156014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
156114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
156214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
156314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
156414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
156514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
156614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
156714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1568f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
156925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1570813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
157114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
157214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
157314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
157414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
15753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
15763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
15783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
15793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
15803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
15825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
15830b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
15840b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
15850b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
1586813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number);
1587813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
15880b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1589653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
15900b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1591285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
159225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1593813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                if (normalizedNumber != null) {
1594813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                    triggerAggregation(rawContactId);
1595813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                }
15960b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
15970b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
15980b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1599653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1600653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1601653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1602653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1603813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1604f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1605813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String number = null;
1606813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String normalizedNumber = null;
16070b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1608813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                number = values.getAsString(Phone.NUMBER);
1609813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                normalizedNumber = computeNormalizedNumber(number);
1610813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1611813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1612653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1613813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1614813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1615813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1616653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1617813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1618813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1619813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
16200b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1621285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
162225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1623813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
16240b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1625813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
162614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
162714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
162814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
162914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
163014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
163114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
163214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
163314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
163414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
163514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1636285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
163725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1638813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
163914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1640653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1641653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1642813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        private String computeNormalizedNumber(String number) {
1643e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
1644e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1645e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1646e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1647653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return normalizedNumber;
1648653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1649e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1650653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1651653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                String number, String normalizedNumber) {
1652e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1653653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
16545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1655653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1656e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
165736045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.MIN_MATCH,
165836045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                        PhoneNumberUtils.toCallerIDMinMatch(number));
165936045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov
1660653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1661653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else {
16624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
16634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=?", mSelectionArgs1);
1664e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
16653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
16663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
16673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
16683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
16693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
16703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
16713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
16723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
16733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
16743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
16753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
16763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
16773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
16783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
16793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
16803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
16813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
16823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1683653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1684653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1685653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1686653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1687653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1688653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1689653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1690653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1691653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
16920be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
16930be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
16940be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1695653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1696653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1697653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1698813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1699f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
170014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1701653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1702813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1703813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1704813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
17050be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
1706813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
17070be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
17080be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
17090be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
17100be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
17110be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
17120be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
17130be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
17140be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
17150be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
17160be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
17170be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
1718b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
17190be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
1720b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
17210be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1722653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1723653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1724653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1725653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1726653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1727653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1728653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1729653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1730653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1731653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1732653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1733653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1734653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1735653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1736653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1737653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1738653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1739653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1740653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1741653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1742653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1743653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1744653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1745653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1746ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
1747ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        mInsertedRawContacts.get(rawContactId));
1748653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1749653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1750653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1751653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1752653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1753653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1754a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1755a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1756a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1757a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1758a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1759a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1760a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1761a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1762a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1763285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1764285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1765285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1766a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1767a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1768a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1769a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1770813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1771f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1772a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1773813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1774813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1775813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1776813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1777a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1778813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1779a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1780a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1781a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1782a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1783a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1784a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1785a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1786a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1787a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1788a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1789a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1790ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    /**
1791ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1792ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1793ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1794ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    public class GroupIdCacheEntry {
1795ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1796ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1797ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1798ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1799ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1800a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
18013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1802b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
180331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
18044097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1805f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1806315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1807622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1808622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1809ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1810ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1811ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // is a list of groups with this group id.
1812ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1813ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
1814622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1815f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1816a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1817d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1818a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
181920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
18201129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private CharArrayBuffer mCharArrayBuffer = new CharArrayBuffer(128);
18215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private NameSplitter.Name mName = new NameSplitter.Name();
182273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
182320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
182409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
182509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
1826ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
182773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1828ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<Long, Account> mInsertedRawContacts = Maps.newHashMap();
1829b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1830a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private HashSet<Long> mDirtyRawContacts = Sets.newHashSet();
1831b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1832de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
18331a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
18341a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
183581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
183681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
18374cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
1838d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
183973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
18404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
18414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1842de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1843ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1844ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1845ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1846ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1847ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1848ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1849ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
185035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1851ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
1852de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
1853b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
1854a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1855b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1856d076a108d58b30591f197e1b90fa8de60999c499Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper);
18570e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1858a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1859b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
1860653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
186151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
1862d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1863b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mSetPrimaryStatement = mDb.compileStatement(
1864653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1865653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1866653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1867653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1868653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1869b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mSetSuperPrimaryStatement = mDb.compileStatement(
1870653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1871653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1872653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1873653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1874653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1875653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1876653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1877653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1878653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1879653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1880653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1881b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mRawContactDisplayNameUpdate = mDb.compileStatement(
188225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
18835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                " SET " +
18845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_SOURCE + "=?," +
18855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_PRIMARY + "=?," +
18865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_ALTERNATIVE + "=?," +
18875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.PHONETIC_NAME + "=?," +
18885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.PHONETIC_NAME_STYLE + "=?," +
18895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.SORT_KEY_PRIMARY + "=?," +
18905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.SORT_KEY_ALTERNATIVE + "=?" +
189125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
18923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1893b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mLastStatusUpdate = mDb.compileStatement(
1894a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                "UPDATE " + Tables.CONTACTS +
1895a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1896a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "(SELECT " + DataColumns.CONCRETE_ID +
1897a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " FROM " + Tables.STATUS_UPDATES +
1898a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.DATA +
1899a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1900a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1901a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
1902a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1903a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1904a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
19050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
19060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                                + StatusUpdates.STATUS +
1907a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                        " LIMIT 1)" +
1908a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1909e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1910b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mNameLookupInsert = mDb.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1911f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1912f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1913f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1914b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mNameLookupDelete = mDb.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1915f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1916f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1917b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateInsert = mDb.compileStatement(
1918a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1919a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
19200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
19210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
19220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
19230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
19240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?)");
1925a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1926b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateReplace = mDb.compileStatement(
1927a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1928a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
19290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
19300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
19310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
19320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
19330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
19340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1935a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1936b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateAutoTimestamp = mDb.compileStatement(
1937a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
19380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
19390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
1940a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
19410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
19420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
1943b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusAttributionUpdate = mDb.compileStatement(
19440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
19450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
19460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
19470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
19480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1949a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1950b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateDelete = mDb.compileStatement(
1951a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1952a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1953a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1954f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        // When setting NAME_VERIFIED to 1 on a raw contact, reset it to 0
1955f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        // on all other raw contacts in the same aggregate
1956b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mResetNameVerifiedForOtherRawContacts = mDb.compileStatement(
1957f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
1958f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " SET " + RawContacts.NAME_VERIFIED + "=0" +
1959f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " WHERE " + RawContacts.CONTACT_ID + "=(" +
1960f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        "SELECT " + RawContacts.CONTACT_ID +
1961f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1962f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        " WHERE " + RawContacts._ID + "=?)" +
1963f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " AND " + RawContacts._ID + "!=?");
1964f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
19653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers = new HashMap<String, DataRowHandler>();
19663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1967e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
19683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
19693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
197067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
197167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
19723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
19733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
197414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
19753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
19763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                new StructuredNameRowHandler(mNameSplitter));
1977622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
1978622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                new StructuredPostalRowHandler(mPostalSplitter));
1979a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
1980a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
19813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1982f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1983f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
19841129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdStructuredName = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE);
19851129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdOrganization = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE);
19861129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdNickname = mDbHelper.getMimeTypeId(Nickname.CONTENT_ITEM_TYPE);
19871129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdPhone = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
198804b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov
1989bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        verifyAccounts();
1990bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        verifyLocale();
1991bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
199265ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
199365ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov            importLegacyContactsAsync();
199465ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        }
199565ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
1996b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return (mDb != null);
19974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
19984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
199951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
200051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
200151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
200204b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
20034cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
200404b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
20054cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
20064cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
200751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
2008cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
20094cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
20104cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
20114cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    @Override
201251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    public void onConfigurationChanged(Configuration newConfig) {
201351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
201451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        verifyLocale();
20154cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
201651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2017c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
2018c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
2019c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
2020c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
2021c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
202251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
202351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
202451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
202551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
202651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
202751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
202851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
202951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void verifyLocale() {
2030f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2031f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
2032f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
2033f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
2034f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
2035f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
203651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
203751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
203851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (providerLocale == null) {
203951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            // The provider has just been created for the first time. There are no
204051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            // contacts in the database, so we can safely set locale on the UI thread.
204151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            mDbHelper.setLocale(ContactsProvider2.this, mCurrentLocale);
204251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
204351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
204451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
204551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
204651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
204751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
204851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
204951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
205051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
205151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
205251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
205351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
205451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        AsyncTask<Integer, Void, Void> task = new AsyncTask<Integer, Void, Void>() {
205551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
205651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            int savedProviderStatus;
205751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
205851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
205951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected Void doInBackground(Integer... params) {
206051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                savedProviderStatus = params[0];
206151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, currentLocale);
206251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                return null;
206351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
206451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
206551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
206651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected void onPostExecute(Void result) {
206751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).commit();
206851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                setProviderStatus(savedProviderStatus);
2069f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2070f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // Recursive invocation, needed to cover the case where locale
2071f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // changes once and then changes again before the db upgrade is completed.
2072f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                verifyLocale();
207351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
207451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        };
207551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
207651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        task.execute(providerStatus);
207751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
207851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
207931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
2080de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
2081b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
2082b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
208331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
208431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
2085013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
2086013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
2087013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
2088013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
20895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
20905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
20915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
20925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
20935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
20943d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
2095b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
2096b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
20973d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
20983d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2099568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
2100568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
2101568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2102568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2103568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2104568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
2105568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
2106568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2107568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
2108bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
2109bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
2110bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch == null) {
2111bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            mAccessLatch = new CountDownLatch(1);
2112bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2113568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2114568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
2115568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
2116568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
2117bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                LegacyContactImporter importer = getLegacyContactImporter();
2118bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (importLegacyContacts(importer)) {
2119bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportSuccess();
2120bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2121bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportFailure();
2122568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
2123568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
2124568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
2125568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2126568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
2127568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2128568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2129bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2130bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
2131bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2132bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
2133bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2134bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
2135bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
2136bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2137b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
2138b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
2139b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
2140bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
2141bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch.countDown();
2142bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch = null;
2143bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
2144bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
2145bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2146bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2147bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
2148bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2149bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
2150bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
2151bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2152bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
2153bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2154bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
2155bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
2156bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
2157bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
2158bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
2159bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
2160bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
2161bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
2162bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
2163bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2164bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
2165bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2166bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
2167bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
21683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
21693d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
21703d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
2171568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
21720e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
21733d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
21743d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
2175bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
2176bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2177bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
2178bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
2179bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
2180bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
21813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
21823d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
21833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
2184bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
2185bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
21863d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
21873d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2188a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
2189a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
2190a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
2191a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
2192b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
2193a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
2194a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2195568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2196568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
2197568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
2198568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
2199568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
2200568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
2201568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2202568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
2203ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
2204ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
2205ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
2206ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
2207ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
2208ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
2209ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
2210ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
221181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
2212ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
2213ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
2214568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
2215568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2216568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2217568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2218568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
2219568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2220568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
2221568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2222568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2223568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2224568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
2225bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch != null) {
2226bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2227bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2228bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2229bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2230bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (match == PROVIDER_STATUS && isLegacyContactImportNeeded()) {
2231bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2232bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2233bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    importLegacyContactsAsync();
2234bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2235bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2236bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2237bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2238bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2239bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2240568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2241568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
2242568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2243568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2244568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2245568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
2246568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2247568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
2248568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2249568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2250568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2251568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2252568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
2253568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2254568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
2255568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2256568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
22574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2258285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
2259bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2260b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2261b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2262285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
22631ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
2264b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2265b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2266b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2267b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
2268285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
2269b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
2270df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
2271a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.clear();
2272285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2273285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2274285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2275285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
22761129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
2277bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2278b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2279b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2280285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
2281b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
22821ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
22831a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
22841a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
2285b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
22861a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
2287b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2288b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2289b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2290bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2291b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2292b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
22931129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
229408e42c9c153a60bf2e7c71dd40bf84bb5fc93555Dmitri Plotnikov        for (long rawContactId : mInsertedRawContacts.keySet()) {
2295d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            updateRawContactDisplayName(mDb, rawContactId);
2296d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
2297285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
2298b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2299a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        if (!mDirtyRawContacts.isEmpty()) {
2300a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2301a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2302a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mDirtyRawContacts);
2303a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2304a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2305a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2306a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2307b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
2308a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2309a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2310a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mUpdatedRawContacts);
2311a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2312a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2313b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2314b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2315b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
2316b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
23179d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
23189d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
23199d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
23209d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2321b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2322b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2323b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2324b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2325b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2326a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2327a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2328a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2329a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2330a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private void appendIds(StringBuilder sb, HashSet<Long> ids) {
2331b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2332a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2333b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2334a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2335a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2336285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2337285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2338285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2339cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
234081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
234181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
234281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
234381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
234481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
234581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
234681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2347cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2348568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
234951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
235051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mProviderStatus = status;
235151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.ProviderStatus.CONTENT_URI,
235251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                null, false);
235351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
235451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2355285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
2356ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return mInsertedRawContacts.containsKey(rawContactId);
2357285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2358285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
23593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
23603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
23613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
23623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
23633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
23643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
23653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
23663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
23673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
23684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2369de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2370bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
23711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2372b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2373f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2374f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2375f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2376f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2377a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2378a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
237935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2380a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
238135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2382b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
238335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
238435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2385d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2386d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
23876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
23886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
23896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
23905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2391f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertRawContact(uri, values);
2392f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2393a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2394a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2395a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
23965ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
23975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2398f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2399f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2400a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2401a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2402a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2403a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2404f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2405f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2406a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2407a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2408a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2409ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2410f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2411f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2412ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2413ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2414ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2415eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
24165aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
241743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2418eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2419eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2420eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
242182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
242282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
24231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
24241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
24251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2426a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
242781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2428f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2429a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2430a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
24327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
24337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2435de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2436a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2437a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2438a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2439e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2440e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2441e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2442e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2443e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2444e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2445e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2446e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2447e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2448e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2449e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2450e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2451e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
24527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2453e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2454f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2455f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2456e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2457f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2458f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2459f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2460e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2461e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2462e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2463e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2464e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
2465fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2466fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2467e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2468e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2469e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2470e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2471e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2472e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2473e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2474e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2475e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2476e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2477e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2478e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
2479fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2480fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2481e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2482e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2483e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2484f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2485f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2486e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2487f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2488f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2489e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2490e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2491f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2492f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2493e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2494f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2495f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2496f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2497f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2498035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2499f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2500e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
25017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
25027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2504d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
25056bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
25066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
25076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
25086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2509d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2510de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
25116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
25126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
25136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2514a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2515a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2516f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2517f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2518a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2519a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2520f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertRawContact(Uri uri, ContentValues values) {
2521f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2522f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2523f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2524f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2525e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
25267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25273d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
25283d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2529f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
25303d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
25313d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2532f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2533f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
2534f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2535f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2536f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2537f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2538285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2539285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2540e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mInsertedRawContacts.put(rawContactId, account);
2541f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2542023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2543a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2544a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2545a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2546a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2547a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2548a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2549a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2550a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2551f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2552a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2553de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2554de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
255567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2556de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
255720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2558de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2559de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2560de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2561b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2562de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2563de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2564508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2565de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2566de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2567de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2568de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2569de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
25704097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2571b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2572de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2573a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2574a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2575a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2576f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2577de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2578a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2579b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2580a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
25814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
25824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
25838e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
25848e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
25858e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
25868e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
25878e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2588b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
2589f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
25908e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
25918e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
25928e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
25938e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
2594f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId, aggregationMode);
2595f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
25968e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
25978e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
25988e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
2599b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
2600f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
26018e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
26028e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
26038e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2604f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
26058e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2606f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2607c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
2608b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
26098e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
2610f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
26118e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2612f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2613f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2614f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2615a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
26165ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
26179261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
26189261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
26195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
26209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
26219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
26229261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
26239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
26249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
2625ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
2626ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Account account) {
2627ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2628ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (account == null) {
26294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
2630ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
26314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
2632ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            try {
2633ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                if (c.moveToFirst()) {
2634ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
2635ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
2636ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2637ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        account = new Account(accountName, accountType);
2638ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    }
26399261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2640ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            } finally {
2641ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                c.close();
26429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
26439261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2644ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
26459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
26469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
2647ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    + "has a sourceid the the contact must be associated with "
26489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
26499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
26509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2651ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
2652ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (entries == null) {
2653ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            entries = new ArrayList<GroupIdCacheEntry>(1);
2654ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            mGroupIdCache.put(sourceId, entries);
2655ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2656ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2657ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int count = entries.size();
2658ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        for (int i = 0; i < count; i++) {
2659ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            GroupIdCacheEntry entry = entries.get(i);
2660ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) {
2661ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                return entry.groupId;
2662ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            }
2663ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2664ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2665ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        GroupIdCacheEntry entry = new GroupIdCacheEntry();
2666ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountName = account.name;
2667ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountType = account.type;
2668ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.sourceId = sourceId;
2669ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entries.add(0, entry);
2670ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
26719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
26725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
2673ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
26749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2675df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
26769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
2677ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (c.moveToFirst()) {
2678ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = c.getLong(0);
26799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
26809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2681df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2682df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
26839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
26849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
26859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
26869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
26879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
26889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2689ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = groupId;
26909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
26919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
26929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
26939261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2694ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2695ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return entry.groupId;
26969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
26979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2698d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private interface DisplayNameQuery {
26991129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        public static final String RAW_SQL =
27001129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                "SELECT "
27011129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + DataColumns.MIMETYPE_ID + ","
27021129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.IS_PRIMARY + ","
27031129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.DATA1 + ","
27045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA2 + ","
27055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA3 + ","
27065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA4 + ","
27075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA5 + ","
27085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA6 + ","
27095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA7 + ","
27105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA8 + ","
27115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA9 + ","
27125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA10 + ","
27135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA11 +
27141129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " FROM " + Tables.DATA +
27151129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " WHERE " + Data.RAW_CONTACT_ID + "=?" +
27161129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        " AND (" + Data.DATA1 + " NOT NULL OR " +
27171129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                Organization.TITLE + " NOT NULL)";
2718d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2719d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int MIMETYPE = 0;
2720d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int IS_PRIMARY = 1;
27215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int DATA1 = 2;
27225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int GIVEN_NAME = 3;                         // data2
27235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int FAMILY_NAME = 4;                        // data3
27245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PREFIX = 5;                             // data4
27255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int TITLE = 5;                              // data4
27265dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int MIDDLE_NAME = 6;                        // data5
27275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int SUFFIX = 7;                             // data6
27285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_GIVEN_NAME = 8;                // data7
27295dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_MIDDLE_NAME = 9;               // data8
27305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int ORGANIZATION_PHONETIC_NAME = 9;         // data8
27315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_FAMILY_NAME = 10;              // data9
27325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int FULL_NAME_STYLE = 11;                   // data10
27335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int ORGANIZATION_PHONETIC_NAME_STYLE = 11;  // data10
27345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_NAME_STYLE = 12;               // data11
2735d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2736d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2737d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    /**
2738d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * Updates a raw contact display name based on data rows, e.g. structured name,
2739d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * organization, email etc.
2740d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     */
2741ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
2742bca1c8b44f99528fc123d5547723e44771e8e934Mike Lockwood        int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
27435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        NameSplitter.Name bestName = null;
27445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String bestDisplayName = null;
27455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String bestPhoneticName = null;
27465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        int bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
2747d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
27481129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(rawContactId);
27491129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        Cursor c = db.rawQuery(DisplayNameQuery.RAW_SQL, mSelectionArgs1);
2750d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        try {
2751d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            while (c.moveToNext()) {
27521129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                int mimeType = c.getInt(DisplayNameQuery.MIMETYPE);
27535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                int source = getDisplayNameSource(mimeType);
27545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (source < bestDisplayNameSource || source == DisplayNameSources.UNDEFINED) {
27555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    continue;
27565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                }
27571129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
27585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (source == bestDisplayNameSource && c.getInt(DisplayNameQuery.IS_PRIMARY) == 0) {
27595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    continue;
2760d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
27611129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
27625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (mimeType == mMimeTypeIdStructuredName) {
27635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    NameSplitter.Name name;
27645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (bestName != null) {
27655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name = new NameSplitter.Name();
27665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    } else {
27675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name = mName;
27685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name.clear();
27695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
27705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.prefix = c.getString(DisplayNameQuery.PREFIX);
27715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.givenNames = c.getString(DisplayNameQuery.GIVEN_NAME);
27725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.middleName = c.getString(DisplayNameQuery.MIDDLE_NAME);
27735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.familyName = c.getString(DisplayNameQuery.FAMILY_NAME);
27745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.suffix = c.getString(DisplayNameQuery.SUFFIX);
27755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.fullNameStyle = c.isNull(DisplayNameQuery.FULL_NAME_STYLE)
27765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            ? FullNameStyle.UNDEFINED
27775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            : c.getInt(DisplayNameQuery.FULL_NAME_STYLE);
27785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticFamilyName = c.getString(DisplayNameQuery.PHONETIC_FAMILY_NAME);
27795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticMiddleName = c.getString(DisplayNameQuery.PHONETIC_MIDDLE_NAME);
27805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticGivenName = c.getString(DisplayNameQuery.PHONETIC_GIVEN_NAME);
27815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticNameStyle = c.isNull(DisplayNameQuery.PHONETIC_NAME_STYLE)
27825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            ? PhoneticNameStyle.UNDEFINED
27835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            : c.getInt(DisplayNameQuery.PHONETIC_NAME_STYLE);
27845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (!name.isEmpty()) {
27855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayNameSource = source;
27865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestName = name;
27875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
27885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                } else if (mimeType == mMimeTypeIdOrganization) {
27895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    mCharArrayBuffer.sizeCopied = 0;
27905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.DATA1, mCharArrayBuffer);
27915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (mCharArrayBuffer.sizeCopied != 0) {
2792d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                        bestDisplayNameSource = source;
27931129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
27941129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
27955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticName = c.getString(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME);
27965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticNameStyle =
27975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                c.isNull(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME_STYLE)
27985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                    ? PhoneticNameStyle.UNDEFINED
27995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                    : c.getInt(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME_STYLE);
28005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    } else {
28015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        c.copyStringToBuffer(DisplayNameQuery.TITLE, mCharArrayBuffer);
28025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        if (mCharArrayBuffer.sizeCopied != 0) {
28031129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayNameSource = source;
28041129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayName = new String(mCharArrayBuffer.data, 0,
28051129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                    mCharArrayBuffer.sizeCopied);
28065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            bestPhoneticName = null;
28075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
28081129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        }
2809d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                    }
28105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                } else {
28115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    // Display name is at DATA1 in all other types.
28125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    // This is ensured in the constructor.
28135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    mCharArrayBuffer.sizeCopied = 0;
28155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.DATA1, mCharArrayBuffer);
28165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (mCharArrayBuffer.sizeCopied != 0) {
28175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayNameSource = source;
28185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
28195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
28205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticName = null;
28215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
28225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
2823d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
2824d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            }
2825d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2826d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        } finally {
2827d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            c.close();
2828d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        }
2829d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
28305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String displayNamePrimary;
28315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String displayNameAlternative;
28325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String sortKeyPrimary = null;
28335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String sortKeyAlternative = null;
28345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        int displayNameStyle = FullNameStyle.UNDEFINED;
28355dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (bestDisplayNameSource == DisplayNameSources.STRUCTURED_NAME) {
28375dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNameStyle = bestName.fullNameStyle;
28385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.CJK
28395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    || displayNameStyle == FullNameStyle.UNDEFINED) {
28405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.getAdjustedFullNameStyle(displayNameStyle);
28415dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bestName.fullNameStyle = displayNameStyle;
28425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
28435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNamePrimary = mNameSplitter.join(bestName, true);
28455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNameAlternative = mNameSplitter.join(bestName, false);
28465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            bestPhoneticName = mNameSplitter.joinPhoneticName(bestName);
28485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            bestPhoneticNameStyle = bestName.phoneticNameStyle;
28495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
28505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNamePrimary = displayNameAlternative = bestDisplayName;
28515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
28525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (bestPhoneticName != null) {
28545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyPrimary = sortKeyAlternative = bestPhoneticName;
28555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (bestPhoneticNameStyle == PhoneticNameStyle.UNDEFINED) {
28565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bestPhoneticNameStyle = mNameSplitter.guessPhoneticNameStyle(bestPhoneticName);
28575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
28585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
28595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.UNDEFINED) {
28605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.guessFullNameStyle(bestDisplayName);
28615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (displayNameStyle == FullNameStyle.UNDEFINED
28625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        || displayNameStyle == FullNameStyle.CJK) {
28635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    displayNameStyle = mNameSplitter.getAdjustedNameStyleBasedOnPhoneticNameStyle(
28645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            displayNameStyle, bestPhoneticNameStyle);
28655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                }
28665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.getAdjustedFullNameStyle(displayNameStyle);
28675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
28685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.CHINESE) {
28695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                sortKeyPrimary = sortKeyAlternative =
2870cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao                        ContactLocaleUtils.getIntance().getSortKey(
2871cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao                                displayNamePrimary, FullNameStyle.CHINESE);
28725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
28735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
28745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (sortKeyPrimary == null) {
28765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyPrimary = displayNamePrimary;
28775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyAlternative = displayNameAlternative;
28785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
28795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        setDisplayName(rawContactId, bestDisplayNameSource, displayNamePrimary,
28815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameAlternative, bestPhoneticName, bestPhoneticNameStyle,
28825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                sortKeyPrimary, sortKeyAlternative);
2883d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2884d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
28851129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private int getDisplayNameSource(int mimeTypeId) {
28861129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        if (mimeTypeId == mMimeTypeIdStructuredName) {
28871129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.STRUCTURED_NAME;
28881129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdEmail) {
28891129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.EMAIL;
28901129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdPhone) {
28911129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.PHONE;
28921129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdOrganization) {
28931129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.ORGANIZATION;
28941129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdNickname) {
28951129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.NICKNAME;
28961129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else {
28971129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.UNDEFINED;
28981129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        }
28991129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    }
29001129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
29019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
290220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
290320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2904f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
290520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
290620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2907de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2908de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
290914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2910de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2911de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
291214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
291314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2914a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2915a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
2916f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
291788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
291888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
291920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
292020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2921de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
292220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
292320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
292420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
292520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
292620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
292788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
292888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
292988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
293020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2931f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
293288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
293388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
29344da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
29354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=?",
29364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2937f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
293820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
293920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
294020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
294120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
294220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
294314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
294420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
294520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
294620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
294720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
294820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
294920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
295020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
295120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
295220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
29537a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
295420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
295520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
295620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2957a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2958813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return rowHandler.delete(mDb, c);
295920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
296020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
296120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
296220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
296320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
296420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
2965ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
2966ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
2967f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2968f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2969f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2970f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2971e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
2972ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2973ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
2974f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
297567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
2976f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
297767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
2978f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
2979ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2980f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2981f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
298273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
298373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
2984f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
2985ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2986f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
29871a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2988ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
2989ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
2990ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
2991ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2992ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
29935aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
2994e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
29955aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
29961a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
29971a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
2998e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
29991a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
3000e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
3001e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3002e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3003ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
300482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
30051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
300682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
300782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
30080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
30094dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
30104dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
30110a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
301282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
30134dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
30144dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
30154dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
30164dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
30171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
30181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3019dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3020dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
302182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
3022f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
30232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3024dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3025dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3026dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30272526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
30282526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
30291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3030dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3031dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
30330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
30340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
30350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3036dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3037dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3038dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30392526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mMimeTypeIdIm);
3040dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
30412526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mMimeTypeIdEmail);
3042f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3043f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3044f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3045f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3046f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3047f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3048f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
30492526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
30502526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
30512526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
30522526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
30532526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30542526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
30552526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30562526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3057dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
30582526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
30592526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3060dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
30612526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
30622526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3063dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
30642526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
30652526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
30662526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
30672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30682526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
30692526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3070dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
30712526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
30722526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3073dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3074dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
30751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
307682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
30772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
30782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3079dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
308070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
3081f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
308270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
30831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
30841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
3085de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
30862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
3087c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    Contacts.IN_VISIBLE_GROUP + " DESC, " + Data.RAW_CONTACT_ID);
30881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
308967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
30905ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
3091e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
30921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
30931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
30941f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
30951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
30961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
309731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
309831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
309931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
31001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
31011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
310282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3103a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3104a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3105a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3106a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3107a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3108a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3109a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
311082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3111a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3112a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
311382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
311482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
311582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
311682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
311782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3118a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
311982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
312082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
31211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3122a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
3123a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
3124a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3125e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
31260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
312782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
312882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
31290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
31300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
31310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
31320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
31330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
31340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
31350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
31360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
31370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
31380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
31390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
31400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3141a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
3142a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
3143a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
314482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
314582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
3146a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
3147a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
31485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindString(mStatusUpdateReplace, 3, status);
31495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindString(mStatusUpdateReplace, 4, resPackage);
31505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindLong(mStatusUpdateReplace, 5, iconResource);
31515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindLong(mStatusUpdateReplace, 6, labelResource);
3152a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
3153a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
3154a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3155a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
3156a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
31575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateInsert, 2, status);
31585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateInsert, 3, resPackage);
31595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusUpdateInsert, 4, iconResource);
31605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusUpdateInsert, 5, labelResource);
3161a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
3162a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
3163a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
31640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    long timestamp = System.currentTimeMillis();
3165a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
31665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateAutoTimestamp, 2, status);
3167a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
31685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateAutoTimestamp, 4, status);
3169a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
31700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
31715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusAttributionUpdate, 1, resPackage);
31725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusAttributionUpdate, 2, iconResource);
31735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusAttributionUpdate, 3, labelResource);
31740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
31750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.execute();
3176a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
3177e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3178e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3179bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3180a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
3181a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
3182a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
3183a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.execute();
3184a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3185a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3186a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
31871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
31881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
31894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3190de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3191bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3192b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3193b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3194b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3195f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3196f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3197508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3198508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
319935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3200b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
320135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3202b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
3203b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3204b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3205b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3206b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
3207b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3208cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3209cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3210cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3211cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3212cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3213d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3214d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3215cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return deleteContact(contactId);
32166bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
32176bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
32189fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
32192e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
32202e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
32212e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3222fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3223fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
32242e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
32252e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
32262e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
32272e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                return deleteContact(contactId);
32282e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
32292e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
32309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
32319fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
32329fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                long contactId = ContentUris.parseId(uri);
32339fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
32349fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
32359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
32369fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
32379fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
32389fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
32399fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
32409fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
32419fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
32429fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
32439fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
32449fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
324560de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
32469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
32479fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
32489fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
32499fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
32509fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
32519fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
32529fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return deleteContact(contactId);
32539fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
32549fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
32559fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
32569fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
32579fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
32589fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
32599fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
32609fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
32619fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
32629fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
32632971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
32642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
3265fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
3266fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3267e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
32682971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
32692971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
32702971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3271fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3272fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3273fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
32742971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
32752971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
32762971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
32772971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
32782971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
32792971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
32802971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
32815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
32822971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
3283fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
3284fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3285508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3286508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
328720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3288f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3289944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3290f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
329120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
329220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
329348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
329448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
329548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
329648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3297508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3298f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
32994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
33004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3301ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3302ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3303ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3304f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
33055aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
33062971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
33072971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
33082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
33092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
33102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
3311e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
33122971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
33132971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
33145aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
33152971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
33162971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
33172971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
33182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
331981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3320f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
332181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
33222971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3323508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3324508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3325eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
332643880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3327e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3328eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3329eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
333082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
33310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
33321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
33331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
333481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
333581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
33363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
333781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3338508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
33394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
33404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
33411c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3342ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3343b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
334494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
3345de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
334694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
334794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
334894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
334994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3350f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
3351de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
335294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
335394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
335494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3355f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
3356de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
335794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
335894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
33591a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
336094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
336194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
336294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
33635aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
3364e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
33651a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3366e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3367e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3368e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3369cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int deleteContact(long contactId) {
337096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
3371cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
337296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
337396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3374cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3375cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3376cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3377cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                markRawContactAsDeleted(rawContactId);
3378cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3379cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3380cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3381cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3382cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3383cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3384cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3385cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3386fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
33873389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
3388f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
338914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
3390fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
3391fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
3392fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
339333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
3394b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
3395cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return markRawContactAsDeleted(rawContactId);
339633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
339733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
339833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
33990a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
34009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
34019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
34029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
34039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
34049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
34059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
34069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
34079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
34080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
34090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3410cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int markRawContactAsDeleted(long rawContactId) {
341181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
341281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3413cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3414cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3415cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3416cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3417cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3418cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3419cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return updateRawContact(rawContactId, mValues);
3420cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3421cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
34224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3423de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3424de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3425bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3426b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3427b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3428b5a4add17815167d20a90645779df34cdf45280dFred Quintana
342935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
343000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
343100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3432b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3433b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
34341129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3435b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
3436b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3437b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3438b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3439f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3440f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
344100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
344235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3443b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3444b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3445b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3446b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3447b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3448b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3449b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3450b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3451b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3452b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3453b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
345435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3455d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
34568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(values, selection, selectionArgs);
345700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
345800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
345900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3460d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
34618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(ContentUris.parseId(uri), values);
3462c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3463c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3464c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
34652e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
34662e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
34672e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
34682e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
34692e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3470fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3471fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
34722e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
34732e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
34742e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
34758c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(contactId, values);
34762e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
34772e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
34782e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
34797d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
34807d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
34817d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
34827d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
34837d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
34847d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
34857d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
34867d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
34877d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
34887d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
348920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3490944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3491f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
349281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3493f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
349481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
349520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
349620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3497c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
349848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
349948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
350048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
350148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3502f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
350381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3504f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
350581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
350600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
350700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
35087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
35095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
35105ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
35114529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count = updateRawContacts(values, selection, selectionArgs);
35127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
35137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
35147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
35155ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
351633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
35174529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
35184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
35194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
35204529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                                    + " AND(" + selection + ")", selectionArgs);
35214529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
35224da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
35234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1);
35244529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
35257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
35267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
35277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3528ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
35295aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3530f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
353181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3532f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
353381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3534ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3535ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3536ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3537ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3538ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
35394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
35404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
354173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
35425aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
35435aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
354481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3545f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
354681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3547ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3548ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3549ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3550127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3551de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3552b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3553b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3554b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3555eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3556e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3557e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
355843880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3559eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3560eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3561eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
35629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
35639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
35649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
35659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
35669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
356781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
356881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3569f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
357081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
357100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
357200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
357300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
35744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
35754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
35769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
35779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
35789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
35799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
35809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
35819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
35829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
35839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
35849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
35859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
35869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
35879705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
35889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
35899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
35909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
35919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
35929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
35939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
35949705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
35959705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
35969705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
35979705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
35989705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
35999705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36009705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
36019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
36029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
36039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
36049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
36059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
36069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
36079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
36089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
36099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
36129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
36139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
36149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
36159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
36169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
36179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
36189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
36199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
36209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
36219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
36229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
36239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
36249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
36279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
36289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
36299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
36309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
36319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36335aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3634f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
363573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3636ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3637ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
363873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3639f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
364073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
364173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
364273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
364373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
364473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
364573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
364673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
364773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3648ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
36491a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
36501a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
365194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
36526ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
36531129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
36546ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3655e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
36566ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
36576ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
36586ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
36596ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
36606ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
36616ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
36626ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
36636ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
36646ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3665ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
36666ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
36676ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
36686ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
36696ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
36706ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
36716ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
36726ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
36736ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
367494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
367594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
367694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3677b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3678b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3679e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
36801a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
36811a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3682e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3683e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3684e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3685e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
36864529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs) {
36874529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
36884529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
36894529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
36904529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
369173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
36924529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3693b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
369451bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
36954529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
36964529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
36974529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
36984529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
36994529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                updateRawContact(rawContactId, values);
37004529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
37014529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
37024529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
37034529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
37044529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
37054529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
37064529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
37074529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
37084529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
37094529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContact(long rawContactId, ContentValues values) {
371096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
371196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
371219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
371319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
371419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3715ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3716ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
371719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
371819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
371996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
372019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
372119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
372219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3723ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3724ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
372519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
372619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
372719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
372819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
372919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
373019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
373119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3732f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
373396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
37345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3735f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3736f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3737f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3738f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3739f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3740f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
3741f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode);
3742f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3743f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3744433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
37454529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3746433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3747285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
37482b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3749285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3750f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3751f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3752f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3753f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3754f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
3755f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.bindLong(1, rawContactId);
3756f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.bindLong(2, rawContactId);
3757f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.execute();
3758f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3759f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3760f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
376119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
376219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                // undo delete, needs aggregation again.
3763ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                mInsertedRawContacts.put(rawContactId, new Account(accountName, accountType));
376419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
37655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
37665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
376733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
376833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3769321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3770f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
377120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
377220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
377320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
37745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
377520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
377620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
377720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
377820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
377920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3780b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
378120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
378220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
378370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
378470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
378520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
378620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
378720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
378870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
378920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
379070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
379120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
379270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
379320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
379470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
379520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
379620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3797653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
379820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3799653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3800653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
380114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3802653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3803653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
3804f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
380520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3806653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3807653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
380820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
380920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3810653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
381120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
381220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3813f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3814653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3815653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3816321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3817653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
381814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
3819a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3820813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        if (rowHandler.update(mDb, values, c, callerIsSyncAdapter)) {
3821813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
3822813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
3823813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
3824a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
3825321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3826321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
38278c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
38288c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            String[] selectionArgs) {
38298c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3830b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
38318c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
38328c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
38338c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
38348c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
38358c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
38368c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                updateContactOptions(contactId, values);
38378c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
38388c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
38398c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
38408c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
38418c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
38428c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
38438c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
38448c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
38458c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
38468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(long contactId, ContentValues values) {
3847d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
38488c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3849b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3850d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3851b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3852d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3853b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3854d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3855b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3856d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3857b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3858d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3859d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3860d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
38618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3862d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3863d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3864d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
38658c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3866c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
38678c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3868c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3869c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
38704da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
38714da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?", mSelectionArgs1);
38728c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
38738c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
38748c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
38758c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3876b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
38778c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3878b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
38798c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3880b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
38818c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3882b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
38838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3884b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
38858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
38868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
38879b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
38886e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
38899b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
38909b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
38919b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
38929b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
38939b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
38949b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
3895f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3896d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3897127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3898127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
38990c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
39000c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
390180c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
39020c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
39030c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
39040c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
39050c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
39060c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
39070c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
39080c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3909b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3910127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
39110c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
39124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
39134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
39140c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
39154da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
39164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
39170c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
39186bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
39196bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
39200c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
39210c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
39220c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
39230c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3924127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3925127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
39263389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
3927f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1, RawContacts.AGGREGATION_MODE_DEFAULT);
3928f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2, RawContacts.AGGREGATION_MODE_DEFAULT);
3929dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
3930b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId1 = mDbHelper.getContactId(rawContactId1);
39310c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
39320c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
3933b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId2 = mDbHelper.getContactId(rawContactId2);
39340c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
3935127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3936127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3937127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3938127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3939b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3940b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
394170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
3942627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
3943627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        boolean hasUnassignedContacts[] = new boolean[]{false};
394470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
394570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
3946743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts);
3947743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
3948743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
3949743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
3950743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
3951743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
3952743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
3953743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
3954743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
3955743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
395648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
3957627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
3958743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
3959627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
3960627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
3961627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
396270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
396370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
396470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            for (Account account : accountsToDelete) {
39655f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Log.d(TAG, "removing data for removed account " + account);
3966627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                String[] params = new String[] {account.name, account.type};
3967627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3968627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.GROUPS +
3969627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
3970627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
3971627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3972627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.PRESENCE +
3973627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
3974627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                "SELECT " + RawContacts._ID +
3975627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
3976627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3977627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
3978627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3979627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.RAW_CONTACTS +
3980627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
3981627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
3982627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
3983627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.SETTINGS +
3984627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
3985627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
3986743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                mDb.execSQL(
3987743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                        "DELETE FROM " + Tables.ACCOUNTS +
3988743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                        " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
3989743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                        " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
3990627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
3991627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3992627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            if (hasUnassignedContacts[0]) {
3993627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
3994627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                Account primaryAccount = null;
3995627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                for (Account account : accounts) {
399673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    if (isWritableAccount(account.type)) {
3997627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        primaryAccount = account;
3998627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        break;
3999627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    }
4000627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4001627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4002627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (primaryAccount != null) {
4003627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    String[] params = new String[] {primaryAccount.name, primaryAccount.type};
4004627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4005627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
4006627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.RAW_CONTACTS +
4007627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + RawContacts.ACCOUNT_NAME + "=?,"
4008627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + RawContacts.ACCOUNT_TYPE + "=?" +
4009627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " IS NULL" +
4010627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL", params);
4011627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4012627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // We don't currently support groups for unsynced accounts, so this is for
4013627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // the future
4014627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
4015627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.GROUPS +
4016627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + Groups.ACCOUNT_NAME + "=?,"
4017627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + Groups.ACCOUNT_TYPE + "=?" +
4018627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " IS NULL" +
4019627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + Groups.ACCOUNT_TYPE + " IS NULL", params);
4020743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
4021743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL(
4022743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4023743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " IS NULL" +
4024743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL");
4025627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
402670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
4027627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4028609cc5b4c96a6981f675a70ff743ecfec2b448e6Daniel Lehmann            mDbHelper.updateAllVisible();
4029609cc5b4c96a6981f675a70ff743ecfec2b448e6Daniel Lehmann
4030b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
403170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
403270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
403370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
403470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
403573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
403670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4037619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4038619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
4039627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
4040627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
4041743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov    private void findValidAccounts(Set<Account> validAccounts, boolean[] hasUnassignedContacts) {
4042743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
4043743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
4044743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
4045627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4046627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
4047627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (c.isNull(0) && c.isNull(1)) {
4048627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    hasUnassignedContacts[0] = true;
4049627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                } else {
4050627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
4051627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4052627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4053627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4054627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4055627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
4056627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4057627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4058627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    /**
4059622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
4060622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
406167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
406267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
406367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
406467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
406567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
406667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
406767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
406867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
406967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
407067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
407167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
407267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
4073dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
4074622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
4075dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
4076dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
4077622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
4078622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
4079dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
4080622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
4081622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
40824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
40834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
40844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
4085bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4086bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4087bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
40880b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4089b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
409035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4091d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
40921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4093c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4094c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4095619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
4096619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
4097a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
40984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
409935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4100b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
410135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
410235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4103d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4104763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4105619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4106619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4107619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4108d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
41094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4110763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
41114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
41124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
41136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
41146bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
41156bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
41165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
41175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
41185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
41195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
41205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4121fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4122fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
41235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
41245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
41255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
41262d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                    // TODO: pull this out into a method and generalize to not require contactId
41275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
41285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4129763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
41304da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    String[] args;
41314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    if (selectionArgs == null) {
41324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        args = new String[2];
41334da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    } else {
41344da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        args = new String[selectionArgs.length + 2];
41354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
41364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    }
41374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    args[0] = String.valueOf(contactId);
413860de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                    args[1] = Uri.encode(lookupKey);
41394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
41404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
41415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            groupBy, limit);
41425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (c.getCount() != 0) {
41435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
41445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
41455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
41465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    c.close();
41475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
41485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4149763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
41504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
41514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
41524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
41535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
41545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
41555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4156f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4157f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
415842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
4159763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
4160f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
41614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
41624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
41634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4164f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4165f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4166f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
416742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
416842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
416942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
417042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
417142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
417242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
417342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
417442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
417542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
417642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4177ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4178916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4179ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4180916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4181ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
4182916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(qb, uri, projection, filterParam);
4183ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4184ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4185ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4186ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4187ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
41884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
4189ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
4190d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
41914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
41924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4193e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
41945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
41954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
41964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
41974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4198763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4199ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
42005e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
42015e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
42025e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
42035e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    starredProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
42045e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    frequentProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
42055e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
42065e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
42074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
42084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
42094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4210d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
42115e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
42125e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
42134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4214d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4215d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
4216d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
4217763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
42184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
42194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4220d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
42215e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
42225e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
4223d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
4224d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
42254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4226d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4227d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
4228d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
4229d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
42304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
42314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
4232d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
4233d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4234d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
4235d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
4236d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4237d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4238ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4239763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4240b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
424171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
42424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4243b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4244b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4245b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4246b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
4247d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_DATA: {
42484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
424982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
42504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
42514da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
42526bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
42536bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
425400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4255ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_PHOTO: {
42563653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
425782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
42584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
42594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
42603653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
42613653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
42623653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
42633653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
42644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
426582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
426689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
42672815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
42682815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
42692815f58f72f109790585931f601a63ddc02536a5Evan Millar
427048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
427182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
42724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
427348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
42744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
427548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
427648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
427748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4278ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
427982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
428089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
4281ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
42824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
42834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4284a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
42855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
42865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
42875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
42885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
42895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
42907318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
42915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
42925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
42935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
42945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (isPhoneNumber(filterParam)) {
42955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
42965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
42975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
42985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
42995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
43005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
43015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
43025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " FROM " + Tables.PHONE_LOOKUP
43035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
43045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(reversed);
43055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append("')");
43065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
43075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4308a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
4309ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
43105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
4311a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4312a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
4313a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
4314ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4315ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4316ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
43174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
431882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
431989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
43204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
43214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
43224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
432348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
432482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
43254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
43264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
43274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
432848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
432948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
433048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
43315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
433282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
433389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
43344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
433508768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
433608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
433708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
433808768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
43394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
4340ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4341ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4342ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
43435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
434482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
434507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
434607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
434707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
434807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
434907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
435007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
435107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
43525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
435307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
435407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
435507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
435607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
435707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
435807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
435907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
436007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
436107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
436207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
436307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " AND " + Data.DATA1 + " LIKE ");
436407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
436520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
436620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
436720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
436807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
436907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            /*
437007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * Using a UNION instead of an "OR" to make SQLite use the right
437107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * indexes. We need it to use the (mimetype,data1) index for the
437207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * email lookup (see above), but not for the name lookup.
437307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * SQLite is not smart enough to use the index on one side of an OR
437407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * but not on the other. Using two separate nested queries
437507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * and a UNION between them does the job.
437607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             */
437707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            sb.append(
437807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " UNION SELECT " + Data._ID +
437907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " FROM " + Tables.DATA +
438007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " WHERE +" + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
438107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " AND " + Data.RAW_CONTACT_ID + " IN ");
43827318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
438320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
43845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
43855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4386a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
43875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
43885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
4389a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4390a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
4391a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
43925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
43935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
43945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4395ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
439682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
439789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
439889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4399ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4400ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4401ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
440248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
440382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
440548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
440648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
44074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
440848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
440948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
441048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
44115ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4412763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
44134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
44144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
44154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
44165ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
44175ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
4418763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
44194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
44204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
44214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
44224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
44234f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
44245ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
44255ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
442682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44274da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
44284da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
4429e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4430e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4431e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
4432e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
443382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
4434e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4435e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4436e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
44374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
443882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
44404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
4441a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
4442a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
4443a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
4444a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
44454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4446a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
4447a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
4448a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
4449e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
4450a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
4451a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4452e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
4453b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, number);
4454e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
4455e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov
4456e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
4457e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
4458e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
4459a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
4460a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
4461a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4462ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
4463b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4464ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
446589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4466ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4467ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4468ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4469ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
4470b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4471ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
44724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
44734da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
4474ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4475ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4476ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4477ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
4478b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
4479ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
448089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
448189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
4482ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4483ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4484ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4485b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
44860c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
4487b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
4488b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
4489b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
4490b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
449131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
4492d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
44932d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
44942d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
44952d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
44962d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
449731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
4498d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
4499d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
450031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
450131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
450231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
450331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4504763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
45057581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
45067581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
45072d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        maxSuggestions, filter);
450831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
450931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4510eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
4511eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
4512eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
451389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4514e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4515e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
4516e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
4517b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
4518e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
451982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4520b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
4521e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4522e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
452382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4524b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
4525e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4526e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
4527e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4528eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
4529eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
4530eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
453182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
45320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
45335ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
45345ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
45355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
453682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
45370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
45384da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
45394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
45405ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
45415ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
45425ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
4543c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
4544a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
4545c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4546c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4547c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
45482d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
45492d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, lookupKey, projection);
4550c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4551c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
45521b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
4553b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
45541b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
45551b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
45561b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
45571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
4558b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
45591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
45601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
45611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
45621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
45631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
4564b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
45651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
45661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
45671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
45681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
45691b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
4570b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
45711b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
457271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
45731b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
45741b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
45751b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
457646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
457746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
457846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
457946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
458046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
458146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
458246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
458346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
45844da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
45854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
458646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
458746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
458846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
458909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
459009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
459109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
459209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
45934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
4594f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
4595c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
45964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
45974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
4598ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
4599ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
4600ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
4601ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
4602ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4603ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
46045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
46055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
46065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
46075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
46085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
4609038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
4610038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
4611038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
4612038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
46135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
46145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
46154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
46164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
46174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
46184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
46194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
46204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
462109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
462209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
462309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
462409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
462509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
462609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
462709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
462809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
462909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
463009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
463109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
463209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
463309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
463409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
463509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
463609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
463709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4638bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
4639bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
4640bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
4641bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
4642ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4643bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4644bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
4645ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
4646ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4647bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
4648bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
4649bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
4650bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4651de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
4652ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4653ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4654ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
4655ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
4656ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
4657ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
4658ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
4659ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
4660ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
4661ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4662ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
4663ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
4664ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
4665ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
4666ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
4667ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
4668ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
4669ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
4670ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
4671ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
4672ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
4673ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4674ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
4675ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
4676ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4677ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4678bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
4679ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
4680bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
4681bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "SUBSTR(" + sortKey + ",1,1) AS " + AddressBookIndexQuery.LETTER);
4682bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4683bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
4684bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
4685bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
4686bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
4687bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
4688bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
4689bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
4690ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
4691bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "GET_PHONEBOOK_INDEX(SUBSTR(" + sortKey + ",1,1),'" + locale + "')"
4692bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
4693ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
4694ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
4695ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
4696ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4697f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
4698ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
4699ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
4700ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4701ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
4702f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
4703ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
4704ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
4705bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
4706bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
4707bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4708bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
4709bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
4710bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
4711ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
4712f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
4713bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
4714bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
4715bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
4716bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
4717bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
4718bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
4719bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
4720bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
4721bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
4722bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
4723bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4724bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
4725bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
4726bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
4727bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
4728bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4729bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
4730bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
4731bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
4732ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4733ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4734ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            final Bundle bundle = new Bundle();
4735ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
4736f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
4737ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            return new CursorWrapper(cursor) {
4738ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4739ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                @Override
4740ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                public Bundle getExtras() {
4741ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                    return bundle;
4742ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                }
4743ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            };
4744ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
4745f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
4746ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4747ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4748ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
47492d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
475092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
475192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
475292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
475392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
47542d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
47552d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
47565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
47575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
47585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
475992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
476092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
476192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
476292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
476392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
476492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
476592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
476692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
476792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
476892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
476992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
477092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
477192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
477292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
477392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
477492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
477592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
477692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
477792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
47785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
47795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
47805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
47825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
47835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
47855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
47865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
47885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
47895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
47905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
47915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
47925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
47935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
47955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
47965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
47975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
47985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
47995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
48005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
48015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
48025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
48035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
48045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
48055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
480692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
48075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
48085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
48095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
48105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
48115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
48125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
48135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
48145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
48155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
48165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
48175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
48185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
48195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
48205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
48215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
48225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
48235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
48245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
482592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
482692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
48275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
48285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
48295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
48305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
48315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
48325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
48335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
48345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
48355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
48365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
48375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
48385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
48395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
484092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
484192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
48425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
48435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
48445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
48455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
48465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
484792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
48485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
48495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
48505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
48515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
48525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
485392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
48545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
48555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
485692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
485792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
485892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
485992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
48605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
48615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
486292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
486392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
486492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
48655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
48665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
486792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
486892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
48695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
487092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
487192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
487292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
487392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
487492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
487592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
487692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
487792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
487892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
487992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
488092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
488192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
488292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
488392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
488492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
488592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
488692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
488792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
488892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
488992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
489092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
48915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
48925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
489392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
489492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
489592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
489692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
489792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
489892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
489992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
490092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
490192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
490292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
490392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
490492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
490592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
490692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
490792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
490892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
490992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
491092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
491192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
491292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
491392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
49145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
49155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
49165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
49175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
491892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
491992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
49205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
49215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
49225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
49235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
49245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
49255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
49265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
49275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
49295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
49305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
49315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
49325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
49335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
49345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
49355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
49365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
49375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
49385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
493992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
494092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
494192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
49425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
49435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
49445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
49455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
49465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
49475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
49485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
49495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
49505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
49515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
49535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
49545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
495592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
495692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
495792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
495892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
495992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
496092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
496192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
496292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
496392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
496492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
496592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
4966ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
4967ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
4968ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
4969ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
49705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
49715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
49725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
49735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
49745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
49755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
49775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
49785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
49805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
49815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
49835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
49845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
49855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
49865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
49875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
49885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
49895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
49905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
49915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
49925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
49935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
49945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
49955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
49965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
49975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
49985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
49995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
50005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
50015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
50025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
50035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
50045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5005763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5006763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
500782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5008916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5009916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5010916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
5011916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5012916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5013916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
5014916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
5015916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
5016916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
5017916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
5018916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            String[] projection, String filter) {
5019916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5020916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5021916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5022916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5023916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(" JOIN (SELECT " +
5024916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                RawContacts.CONTACT_ID + " AS snippet_contact_id");
5025916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5026916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA_ID)) {
5027916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", " + DataColumns.CONCRETE_ID + " AS "
5028916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    + SearchSnippetColumns.SNIPPET_DATA_ID);
5029916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5030916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
50319c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA1)) {
50329c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA1 + " AS " + SearchSnippetColumns.SNIPPET_DATA1);
5033916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5034916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
50359c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA2)) {
50369c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA2 + " AS " + SearchSnippetColumns.SNIPPET_DATA2);
5037916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5038916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
50399c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA3)) {
50409c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA3 + " AS " + SearchSnippetColumns.SNIPPET_DATA3);
50419c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
50429c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov
50439c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA4)) {
50449c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA4 + " AS " + SearchSnippetColumns.SNIPPET_DATA4);
5045916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5046916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5047916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_MIMETYPE)) {
5048916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", (" +
5049916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    "SELECT " + MimetypesColumns.MIMETYPE +
5050916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " FROM " + Tables.MIMETYPES +
5051916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " WHERE " + MimetypesColumns._ID + "=" + DataColumns.MIMETYPE_ID +
5052916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    ") AS " + SearchSnippetColumns.SNIPPET_MIMETYPE);
5053916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5054916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5055916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS +
5056916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " WHERE " + DataColumns.CONCRETE_ID +
5057916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " IN (");
5058916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5059916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // Construct a query that gives us exactly one data _id per matching contact.
5060916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // MIN stands in for ANY in this context.
5061916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(
5062916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                "SELECT MIN(" + Tables.NAME_LOOKUP + "." + NameLookupColumns.DATA_ID + ")" +
5063916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
5064916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " JOIN " + Tables.RAW_CONTACTS +
5065916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " ON (" + RawContactsColumns.CONCRETE_ID
5066916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                        + "=" + Tables.NAME_LOOKUP + "." + NameLookupColumns.RAW_CONTACT_ID + ")" +
5067916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME + " GLOB '");
5068916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filter));
5069916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
5070916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + ")" +
5071916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " GROUP BY " + RawContactsColumns.CONCRETE_CONTACT_ID);
5072916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5073916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(")) ON (" + Contacts._ID + "=snippet_contact_id)");
5074916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5075916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5076916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
5077916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5078916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5079916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void appendContactsTables(StringBuilder sb, Uri uri, String[] projection) {
5080763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5081f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5082763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5083763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5084d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5085763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5086763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
5087b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
508882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_PRESENCE)) {
508982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
509082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ")");
509182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
5092b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
509382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS,
509482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
509582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
509682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
509782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
50983296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
50993296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
5100a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
51013296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
510282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
510382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
5104ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
5105763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
5106763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
5107763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5108f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5109763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5110763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5111d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5112763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5113763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
5114763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
5115763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
5116763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
5117763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
5118763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
511946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private void setTablesAndProjectionMapForRawContactsEntities(SQLiteQueryBuilder qb, Uri uri) {
512046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        // Note: currently, "export only" equals to "restricted", but may not in the future.
512146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
512246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                Data.FOR_EXPORT_ONLY, false);
512346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
5124f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
512546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
512646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        if (requestingPackage != null) {
512746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            excludeRestrictedData = excludeRestrictedData
512846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
512946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        }
513046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setTables(mDbHelper.getContactEntitiesView(excludeRestrictedData));
513146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setProjectionMap(sRawContactsEntityProjectionMap);
513246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
513346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
513446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
513582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
513682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
513782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5138d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa        // Note: currently, "export only" equals to "restricted", but may not in the future.
5139763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
5140d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa                Data.FOR_EXPORT_ONLY, false);
5141763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5142f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5143763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5144763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5145763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            excludeRestrictedData = excludeRestrictedData
5146d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5147763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5148763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5149763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getDataView(excludeRestrictedData));
515082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
515182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
51523296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated presence when requested
5153b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.CONTACT_PRESENCE)) {
515482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
51553296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + "="
515682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    + RawContacts.CONTACT_ID + ")");
515782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
515882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
51593296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated status updates when requested
5160b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
516182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS,
516282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_RES_PACKAGE,
516382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_ICON,
516482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_LABEL,
516582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_TIMESTAMP)) {
51663296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
51673296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
516882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
51693296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
5170ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        }
51713296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
51723296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual presence when requested
51733296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection, Data.PRESENCE)) {
51743296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
51753296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdates.DATA_ID + "="
51763296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + DataColumns.CONCRETE_ID + ")");
51773296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
51783296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
51793296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual status updates when requested
51803296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection,
51813296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS,
51823296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_RES_PACKAGE,
51833296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_ICON,
51843296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_LABEL,
51853296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_TIMESTAMP)) {
51863296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
51873296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
51883296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + DataColumns.CONCRETE_ID + ")");
51893296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
51903296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
519182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
519282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);
519382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
5194ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
5195ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
51960a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
51970a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
51980a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5199b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
52000a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
52010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5202b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, StatusUpdates.PRESENCE)) {
52030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
52040a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID
52050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
52060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
52070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5208b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
52090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
52100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
52110a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
52120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
52130a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
52140a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
52150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.STATUS_UPDATES + "." + StatusUpdatesColumns.DATA_ID
52160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
52170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
52180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setTables(sb.toString());
52190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
52200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
52210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
52224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
5223f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5224f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5225e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5226e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5227e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5228e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5229fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5230fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5231e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5232e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5233e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5234e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5235e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5236e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
52374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
52384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
52394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
52404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
52414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
52424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
52434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
52444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
52454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5246e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
5247f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5248f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5249e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5250e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5251e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5252e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5253fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5254fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5255e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5256e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5257e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5258e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5259e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5260e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
5261e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
5262e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
5263e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
5264e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
5265e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
5266e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
5267e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
5268e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
5269e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
5270e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
5271e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
5272e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
5273e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
5274e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
5275e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
52767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5277c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
5278c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
5279c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
5280c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
5281c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
5282f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
5283f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String limitParam = getQueryParameter(uri, "limit");
5284c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
5285c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5286c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5287c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
5288c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
5289c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
5290c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
5291c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
5292c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
5293c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5294c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
5295c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
5296c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
5297c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5298c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5299c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5300c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
53015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /**
53025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
53035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
53045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     */
53055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
53065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        int len = cons.length();
53075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
53085e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        for (int i = 0; i < len; i++) {
53095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            char c = cons.charAt(i);
53105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
53115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
53125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
53135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
53145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
53155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    || (c == '#') || (c == '*')) {
53165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
53175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
53185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
53195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
53205e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
53215e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
53225e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
53235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
53245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
53255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            return false;
53265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        }
53275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
53285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        return true;
53295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
53305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
533100ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
5332d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
533370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
533470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
5335fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
533670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
533770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
533870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
533970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
5340d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
534170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
534267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
53435ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
53445ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
5345619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
5346619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
5347619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5348b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
5349b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
5350b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
5351b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
5352d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_PHOTO: {
5353e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                return openPhotoAssetFile(uri, mode,
5354e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " + RawContacts.CONTACT_ID + "=?",
5355e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5356e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
5357b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5358e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
5359e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                return openPhotoAssetFile(uri, mode,
5360e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
53614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5362d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5363d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5364f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
536542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
536642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                mSelectionArgs1[0] = String.valueOf(lookupContactIdByLookupKey(mDb, lookupKey));
536742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + "=?";
536842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
536942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
537042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
537142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
537242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
537342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                outputRawContactsAsVCard(localStream, selection, mSelectionArgs1);
537442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return buildAssetFileDescriptor(localStream);
537542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
537642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
537742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
537842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
537942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
538042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
538142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
538242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String[] selectionArgs = new String[loopupKeyList.length];
538342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
538442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    selectionArgs[index] =
538542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                        String.valueOf(lookupContactIdByLookupKey(mDb, lookupKey));
538642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
538742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                        inBuilder.append("(?");
538842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
538942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                        inBuilder.append(",?");
539042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
539142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
539242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
539342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
539442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
5395d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5396d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
5397d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
5398d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
5399d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
540042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                outputRawContactsAsVCard(localStream, selection, selectionArgs);
5401d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return buildAssetFileDescriptor(localStream);
5402d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5403b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5404b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
5405fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
5406fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
5407b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
5408b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
5409b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5410e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,
5411e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
5412e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
5413e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
5414e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
5415e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
5416e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
5417e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5418e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
5419e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
5420e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
5421e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
5422e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql,
5423e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                selectionArgs);
5424e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
5425e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5426d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
5427d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5428d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5429d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
5430d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
5431d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5432d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
5433d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        AssetFileDescriptor fd = null;
5434d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
5435d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
5436d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5437d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
5438d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final int size = byteData.length;
5439d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5440d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
5441d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.writeBytes(byteData, 0, 0, size);
5442d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.deactivate();
5443b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5444d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
5445d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
5446d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
5447d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5448d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        return fd;
5449d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5450d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5451d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5452d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
5453d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
5454d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
5455d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5456d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
5457d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
5458d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
54597a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
54607a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa                new VCardComposer(context, VCardConfig.VCARD_TYPE_DEFAULT, false);
5461d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
5462d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5463f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
54647a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        if (!composer.init(selection, selectionArgs)) {
54657a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa            Log.w(TAG, "Failed to init VCardComposer");
5466d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
54677a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        }
5468d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5469d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
5470d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
5471d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
5472d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5473d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5474d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
5475d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5476b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
54774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
54784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
5479a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
54804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
5481b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
5482be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
54832d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
5484b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
5485b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
5486b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
5487f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
548842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
5489f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
5490b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
5491be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
5492b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
5493b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
5494508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
5495b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
549648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
549748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
549848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
549948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
55009005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
55019005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
550248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
550348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
550448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
550548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
550648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
550748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
550848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
550948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
5510b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
5511b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
5512b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
5513b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
5514b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
5515b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
5516b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
5517b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
5518c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
5519c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
5520c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
5521c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
5522d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov
552361efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
552461efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
55254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
55264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
55277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
55285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void setDisplayName(long rawContactId, int displayNameSource,
55295dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            String displayNamePrimary, String displayNameAlternative, String phoneticName,
55305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            int phoneticNameStyle, String sortKeyPrimary, String sortKeyAlternative) {
55315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(1, displayNameSource);
55325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 2, displayNamePrimary);
55335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 3, displayNameAlternative);
55345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 4, phoneticName);
55355dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(5, phoneticNameStyle);
55365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 6, sortKeyPrimary);
55375dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 7, sortKeyAlternative);
55385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(8, rawContactId);
553925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
55403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
55413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
554273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
554373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
554473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
554573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
5546a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.add(rawContactId);
554773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
554873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
5549c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
5550c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
5551c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
5552c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
5553c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
5554c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
5555653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
5556c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
5557653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
5558653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
5559c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
5560c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
5561c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
5562c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
5563c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
5564c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
5565c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
5566c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
5567c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
5568653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
5569c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
5570653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
5571653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
5572c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
5573c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
5574ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
5575813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov    public String insertNameLookupForEmail(long rawContactId, long dataId, String email) {
5576f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
5577813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return null;
5578f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5579f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5580b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov        String address = mDbHelper.extractHandleFromEmailAddress(email);
5581b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov        if (address == null) {
5582813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return null;
5583f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5584f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5585f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
5586f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
5587813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        return address;
5588f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5589f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5590f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5591f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
5592f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5593f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
5594f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
5595f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
5596f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5597f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5598f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
5599f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
5600f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5601f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5602a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
5603a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
5604a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
5605a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
5606a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
5607a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
5608a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
5609a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
5610a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
5611a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
5612a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
5613f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5614d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name,
5615d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            int fullNameStyle) {
5616d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name, fullNameStyle);
5617f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5618f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5619f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
5620f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5621f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
5622f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
5623f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5624f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5625f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5626f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
5627f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
5628f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
5629f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5630f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5631f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5632f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
5633d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
5634f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5635f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5636f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
563748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    public void insertNameLookupForPhoneticName(long rawContactId, long dataId,
563848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            ContentValues values) {
563948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (values.containsKey(StructuredName.PHONETIC_FAMILY_NAME)
564048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                || values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)
564148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                || values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME)) {
564248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId,
564348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
564448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
564548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
564648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
564748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    }
564848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
564948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    public void insertNameLookupForPhoneticName(long rawContactId, long dataId, String familyName,
565048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            String middleName, String givenName) {
565148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        mSb.setLength(0);
565248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (familyName != null) {
565348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(familyName.trim());
565448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
565548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (middleName != null) {
565648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(middleName.trim());
565748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
565848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (givenName != null) {
565948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(givenName.trim());
566048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
566148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
566248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (mSb.length() > 0) {
566348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookup(rawContactId, dataId, NameLookupType.NAME_COLLATION_KEY,
566448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    NameNormalizer.normalize(mSb.toString()));
566548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
566648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    }
566748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
5668f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5669f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
5670f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5671f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
56725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(1, rawContactId);
56735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(2, dataId);
56745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(3, lookupType);
56755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mNameLookupInsert, 4, name);
5676f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
5677f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5678f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5679f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5680f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
5681f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5682f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
56835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupDelete.bindLong(1, dataId);
5684f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
5685f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5686f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
56872d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
5688d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5689d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
5690d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
5691d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
5692d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
5693d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
5694d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
5695e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
5696916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
5697916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
5698e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
5699e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
57005ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
5701c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
57027318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam);
5703c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
5704c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5705c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
57067318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam) {
57077318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), true);
57085e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
57095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
57105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
57117318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov            boolean allowEmailMatch) {
5712d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5713dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov                "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
5714d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
5715d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
5716d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " GLOB '");
57175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
5718a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
5719a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NAME_COLLATION_KEY + ","
5720a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NICKNAME + ","
57214cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                + NameLookupType.NAME_SHORTHAND + ","
5722f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                + NameLookupType.ORGANIZATION + ","
572389f1f71495aedc58252b3f58a46a036986c319d2Dmitri Plotnikov                + NameLookupType.NAME_CONSONANTS);
572420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        if (allowEmailMatch) {
572520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
572620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        }
572789f1f71495aedc58252b3f58a46a036986c319d2Dmitri Plotnikov        sb.append("))");
5728ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
5729ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
57304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
57314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
57324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
57334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5734b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
5735b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
5736b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
5737b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
5738b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
57394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
57404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5741b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
5742b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5743b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5744caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
57455e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
57465e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
57475e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
57485e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
57495e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
57505e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
57515e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
57525e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
57535e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
57545e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
57555e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5756caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
5757caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
5758caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
5759df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
5760df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
5761caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
5762caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
5763caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
5764caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
57656f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
5766caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
57676f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
5768caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
5769f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
577073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
577173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
577273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
577373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
577473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
577573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
577673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
577773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
577873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
5779627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
5780627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
5781627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
5782627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
578373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
578473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
578573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
5786627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
5787627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
5788627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
5789627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
5790627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
579173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
579273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
579373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
579473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
579573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
579673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
579773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
5798627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
5799b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
5800f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
5801f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
5802f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5803f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
5804f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5805f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5806f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5807f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5808f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5809f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
5810f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
5811f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5812f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5813f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5814f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
5815f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5816f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
5817f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
5818f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5819f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5820f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
5821f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
5822f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
5823f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
5824f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
5825f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5826f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5827f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
5828f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
5829f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
5830f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
5831f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5832f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5833f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
5834f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5835f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5836f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
5837f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
5838f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5839f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
5840f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
5841f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
5842f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
5843f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
5844f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5845f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5846f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5847f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
5848f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5849f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
5850f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5851f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5852f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5853f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
5854f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
5855f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
5856f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5857f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5858f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5859f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
5860f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
5861f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
5862f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
5863f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
5864f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5865f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5866f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
5867f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
58685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
58695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void bindString(SQLiteStatement stmt, int index, String value) {
58705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (value == null) {
58715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindNull(index);
58725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
58735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindString(index, value);
58745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
58755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
58765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
58775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void bindLong(SQLiteStatement stmt, int index, Number value) {
58785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (value == null) {
58795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindNull(index);
58805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
58815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindLong(index, value.longValue());
58825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
58835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
58844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
5885