ContactsProvider2.java revision 2a0d5f9c628e723261bc5198e0fd606076b76b74
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;
61b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.content.res.AssetFileDescriptor;
624cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Taoimport android.content.res.Configuration;
631129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikovimport android.database.CharArrayBuffer;
644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
65ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
66ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
6709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
6809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
69a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
70b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.database.sqlite.SQLiteContentHelper;
714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
73c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
7551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikovimport android.os.AsyncTask;
766ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
77d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.os.MemoryFile;
78b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
792a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikovimport android.os.SystemClock;
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
150c64e0fb0655d8e81c22db555b55c953b5b787b01Fred Quintana    private static final String GOOGLE_MY_CONTACTS_GROUP_TITLE = "System Group: My Contacts";
1513d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
152b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1533d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1543d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1553d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
156b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
157b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
15851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
1593d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1602a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov    private static final String PROPERTY_AGGREGATION_ALGORITHM = "aggregation_v2";
1612a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov    private static final int PROPERTY_AGGREGATION_ALGORITHM_VERSION = 2;
1622a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
1630e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1640e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
165a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1675e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final String TIMES_CONTACED_SORT_COLUMN = "times_contacted_sort";
1685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
169d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
1705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            + TIMES_CONTACED_SORT_COLUMN + " DESC, "
1719b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
172d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
173d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
174d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
175d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1766e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
1779b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
1789b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1799b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
1809b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
1816e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
1829b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
1839b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1849b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
1859b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
186de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
187de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
188d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
189d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
1925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_DATA = 1004;
1935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
1975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_PHOTO = 1009;
198f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
19942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private static final int CONTACTS_AS_MULTI_VCARD = 1011;
2004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
20446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
2054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
208ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
20948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
21048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
21148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
21248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
21348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
21448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
21548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
21648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
217a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
220b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
221b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
222b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
22382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
22482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
22631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
22731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
228eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
229eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
230ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
231ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
232ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
233ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
23435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
235b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
23635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
237c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
238c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
239c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2411b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
24546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
24646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
24709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
24809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
249d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
250f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
251f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
252f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
25367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
25467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2556cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
257f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
258ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
259ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
260d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
26167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
262d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
263ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2641f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
26514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
26667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
26888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
2693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
2703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
2715ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
2723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
273f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
27488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
27588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
27688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
27788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
27888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
27988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
28088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
281f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
2823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
2833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
28414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
2853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
2865ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
2873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
288f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
2893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
2903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
29114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
292321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
29320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
29420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
295321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
296321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
29720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
29820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
299f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
30019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private interface RawContactsQuery {
30119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
30219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
30319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
304ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
305ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
306ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
30719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
30819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
30919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
310ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
311ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
31219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
31319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
314c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
315df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
316caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
31771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
31871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
31971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
32071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
32171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
32271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
32371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
32471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
32571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
32671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
32771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
32871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
32971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
33071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
331a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
332a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
333a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
334a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
335a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
336a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
337a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
338a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
339a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
340a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
341a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
342a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
343916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
344916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
345916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
346916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
347916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NICKNAME + "," +
348916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_SHORTHAND + "," +
349f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.ORGANIZATION + "," +
350f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.NAME_CONSONANTS;
351916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
352916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
353038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
354038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    private static final HashMap<String, String> sCountProjectionMap;
355e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
3564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
357916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
358916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final HashMap<String, String> sContactsProjectionWithSnippetMap;
359916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
3605e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
3615e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentStarredProjectionMap;
3625e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private static final HashMap<String, String> sStrequentFrequentProjectionMap;
363f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
364f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final HashMap<String, String> sContactsVCardProjectionMap;
365ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
366d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final HashMap<String, String> sRawContactsProjectionMap;
36746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    /** Contains the columns from the raw contacts entity view*/
36846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final HashMap<String, String> sRawContactsEntityProjectionMap;
3694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
3704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private static final HashMap<String, String> sDataProjectionMap;
3715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
3725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private static final HashMap<String, String> sDistinctDataProjectionMap;
3739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
374e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    private static final HashMap<String, String> sPhoneLookupProjectionMap;
375ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
376ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
377ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
378ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
379373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
380b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
381eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
382eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final HashMap<String, String> sSettingsProjectionMap;
38382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
38482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final HashMap<String, String> sStatusUpdatesProjectionMap;
3851b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
3861b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final HashMap<String, String> sLiveFoldersProjectionMap;
3877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
3899705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
3909705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
3919705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
3929705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
3939705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
3942526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
3952526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
396bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
397bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
398bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
399bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
40051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
401c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
402c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
4033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
404c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
4053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
40625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
40782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Precompiled sql statement for updating an aggregated status update */
408a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mLastStatusUpdate;
409f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
410f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupDelete;
411a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateAutoTimestamp;
412a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateInsert;
413a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateReplace;
4140a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private SQLiteStatement mStatusAttributionUpdate;
415a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateDelete;
416f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov    private SQLiteStatement mResetNameVerifiedForOtherRawContacts;
417a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
418f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdEmail;
419f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdIm;
4201129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdStructuredName;
4211129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdOrganization;
4221129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdNickname;
4231129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdPhone;
424f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
4251129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
4261129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
4272526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
4282526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
429f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
430f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
4314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
4324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
433a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
434d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
435d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
436d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
4373653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
4383653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
4392d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
4402d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
4413653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO);
4425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
4435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
4445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
445f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
44642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
44742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
4485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
449ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
450ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
4515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
4523653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4535ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
4545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
4555ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
45646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
45746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
45846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
459b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
4604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
4614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
462ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
46348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
4645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
465ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
4664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
46748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
4685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
4695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
4704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
471ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
47248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
4731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
474ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
475ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
476ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
477ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
47835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
479b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
480b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
48135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
482a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
483b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
484b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
485b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
486b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
4874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
488eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
489eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
49082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
49182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
4921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
493c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
494c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
495c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
496c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
4972d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
498c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
499c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
5001b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
5011b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
5021b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
5031b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
5041b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
5051b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
5061b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
5071b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
50809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
50909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
51019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
51119a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
51219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    static {
513038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap = new HashMap<String, String>();
514038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
515e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
5164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap = new HashMap<String, String>();
5174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts._ID, Contacts._ID);
5185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME_PRIMARY);
5195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME_ALTERNATIVE,
5205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE);
5215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.DISPLAY_NAME_SOURCE, Contacts.DISPLAY_NAME_SOURCE);
5225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHONETIC_NAME, Contacts.PHONETIC_NAME);
5235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHONETIC_NAME_STYLE, Contacts.PHONETIC_NAME_STYLE);
5245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SORT_KEY_PRIMARY, Contacts.SORT_KEY_PRIMARY);
5255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SORT_KEY_ALTERNATIVE, Contacts.SORT_KEY_ALTERNATIVE);
5264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
5274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
5284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
5294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
5304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
5314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
532f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        sContactsProjectionMap.put(Contacts.HAS_PHONE_NUMBER, Contacts.HAS_PHONE_NUMBER);
5334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sContactsProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
5345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sContactsProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
535f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
5363296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
5373296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_PRESENCE,
5383296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
539aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        addProjection(sContactsProjectionMap, Contacts.CONTACT_CHAT_CAPABILITY,
540aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY);
5413296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS,
5423296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
5433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
5443296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
5453296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
5463296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
5473296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_LABEL,
5483296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
5493296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sContactsProjectionMap, Contacts.CONTACT_STATUS_ICON,
5503296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
5513296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
552916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sContactsProjectionWithSnippetMap = new HashMap<String, String>();
553916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sContactsProjectionWithSnippetMap.putAll(sContactsProjectionMap);
554916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_MIMETYPE,
555916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_MIMETYPE);
556916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_DATA_ID,
557916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_DATA_ID);
5589c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_DATA1,
5599c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_DATA1);
5609c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_DATA2,
5619c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_DATA2);
5629c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_DATA3,
5639c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_DATA3);
5649c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        sContactsProjectionWithSnippetMap.put(SearchSnippetColumns.SNIPPET_DATA4,
5659c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov                SearchSnippetColumns.SNIPPET_DATA4);
566916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5675e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
5685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentStarredProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
5695e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Long.MAX_VALUE + " AS " + TIMES_CONTACED_SORT_COLUMN);
5705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5715e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap = new HashMap<String, String>(sContactsProjectionMap);
5725e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        sStrequentFrequentProjectionMap.put(TIMES_CONTACED_SORT_COLUMN,
5735e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                  Contacts.TIMES_CONTACTED + " AS " + TIMES_CONTACED_SORT_COLUMN);
5745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
575f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap = Maps.newHashMap();
576f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME
577d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                + " || '.vcf' AS " + OpenableColumns.DISPLAY_NAME);
578ba355248c255551bc65d8023b968513cbe9bcdf3Jeff Sharkey        sContactsVCardProjectionMap.put(OpenableColumns.SIZE, "NULL AS " + OpenableColumns.SIZE);
5794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap = new HashMap<String, String>();
5814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts._ID, RawContacts._ID);
5824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
5834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
5844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
5854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
5864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
5874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
5884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DELETED, RawContacts.DELETED);
5895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DISPLAY_NAME_PRIMARY,
5905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.DISPLAY_NAME_PRIMARY);
5915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DISPLAY_NAME_ALTERNATIVE,
5925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.DISPLAY_NAME_ALTERNATIVE);
5935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.DISPLAY_NAME_SOURCE,
5945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.DISPLAY_NAME_SOURCE);
5955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.PHONETIC_NAME,
5965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.PHONETIC_NAME);
5975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.PHONETIC_NAME_STYLE,
5985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.PHONETIC_NAME_STYLE);
599f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.NAME_VERIFIED,
600f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                RawContacts.NAME_VERIFIED);
6015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SORT_KEY_PRIMARY,
6025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.SORT_KEY_PRIMARY);
6035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SORT_KEY_ALTERNATIVE,
6045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                RawContacts.SORT_KEY_ALTERNATIVE);
6054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.TIMES_CONTACTED, RawContacts.TIMES_CONTACTED);
6064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.LAST_TIME_CONTACTED,
6074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.LAST_TIME_CONTACTED);
6084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.CUSTOM_RINGTONE, RawContacts.CUSTOM_RINGTONE);
6094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SEND_TO_VOICEMAIL, RawContacts.SEND_TO_VOICEMAIL);
6104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.STARRED, RawContacts.STARRED);
6114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE);
6124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC1, RawContacts.SYNC1);
6134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC2, RawContacts.SYNC2);
6144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC3, RawContacts.SYNC3);
6154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sRawContactsProjectionMap.put(RawContacts.SYNC4, RawContacts.SYNC4);
6162815f58f72f109790585931f601a63ddc02536a5Evan Millar
6174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap = new HashMap<String, String>();
6184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data._ID, Data._ID);
6194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RAW_CONTACT_ID, Data.RAW_CONTACT_ID);
6204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
6214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
6224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
6234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
6244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
6254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA1, Data.DATA1);
6264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA2, Data.DATA2);
6274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA3, Data.DATA3);
6284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA4, Data.DATA4);
6294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA5, Data.DATA5);
6304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA6, Data.DATA6);
6314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA7, Data.DATA7);
6324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA8, Data.DATA8);
6334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA9, Data.DATA9);
6344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA10, Data.DATA10);
6354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA11, Data.DATA11);
6364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA12, Data.DATA12);
6374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA13, Data.DATA13);
6384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA14, Data.DATA14);
6394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.DATA15, Data.DATA15);
6404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
6414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
6424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
6434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
64482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sDataProjectionMap.put(Data.CONTACT_ID, Data.CONTACT_ID);
6454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
6464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
6474a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
6484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.VERSION, RawContacts.VERSION);
6494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.DIRTY, RawContacts.DIRTY);
650f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        sDataProjectionMap.put(RawContacts.NAME_VERIFIED, RawContacts.NAME_VERIFIED);
65156d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
6524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
6535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME_ALTERNATIVE,
6545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE);
6555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.DISPLAY_NAME_SOURCE, Contacts.DISPLAY_NAME_SOURCE);
6565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHONETIC_NAME, Contacts.PHONETIC_NAME);
6575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHONETIC_NAME_STYLE, Contacts.PHONETIC_NAME_STYLE);
6585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SORT_KEY_PRIMARY, Contacts.SORT_KEY_PRIMARY);
6595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SORT_KEY_ALTERNATIVE, Contacts.SORT_KEY_ALTERNATIVE);
6604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
6614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
6624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
6634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
6644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
6654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
666a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
667afbf2a3343d0f8e7ae7cbfbbec60004ed37caf3fDaniel Lehmann        sDataProjectionMap.put(Contacts.NAME_RAW_CONTACT_ID, Contacts.NAME_RAW_CONTACT_ID);
6684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        sDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
669a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
67046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        HashMap<String, String> columns;
67146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns = new HashMap<String, String>();
67246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts._ID, RawContacts._ID);
67346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
67446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_NAME);
67546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_TYPE);
67646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SOURCE_ID, RawContacts.SOURCE_ID);
67746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.VERSION, RawContacts.VERSION);
67846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DIRTY, RawContacts.DIRTY);
67946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.DELETED, RawContacts.DELETED);
680bf6a7e4dece49ba4e7cda17f7ed9250aeb82f731Jeff Sharkey        columns.put(RawContacts.IS_RESTRICTED, RawContacts.IS_RESTRICTED);
68146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC1, RawContacts.SYNC1);
68246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC2, RawContacts.SYNC2);
68346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC3, RawContacts.SYNC3);
68446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.SYNC4, RawContacts.SYNC4);
685f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        columns.put(RawContacts.NAME_VERIFIED, RawContacts.NAME_VERIFIED);
68646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
68746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.MIMETYPE, Data.MIMETYPE);
68846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA1, Data.DATA1);
68946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA2, Data.DATA2);
69046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA3, Data.DATA3);
69146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA4, Data.DATA4);
69246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA5, Data.DATA5);
69346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA6, Data.DATA6);
69446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA7, Data.DATA7);
69546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA8, Data.DATA8);
69646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA9, Data.DATA9);
69746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA10, Data.DATA10);
69846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA11, Data.DATA11);
69946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA12, Data.DATA12);
70046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA13, Data.DATA13);
70146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA14, Data.DATA14);
70246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA15, Data.DATA15);
70346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC1, Data.SYNC1);
70446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC2, Data.SYNC2);
70546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC3, Data.SYNC3);
70646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.SYNC4, Data.SYNC4);
70746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(RawContacts.Entity.DATA_ID, RawContacts.Entity.DATA_ID);
70846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.STARRED, Data.STARRED);
70946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
71046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
71146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
71246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        columns.put(GroupMembership.GROUP_SOURCE_ID, GroupMembership.GROUP_SOURCE_ID);
71346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        sRawContactsEntityProjectionMap = columns;
71446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
7153296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
7163296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_PRESENCE,
7173296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
718aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        addProjection(sContactsProjectionMap, Contacts.CONTACT_CHAT_CAPABILITY,
719aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY);
7203296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS,
7213296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
7223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
7233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
7253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7263296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
7273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7283296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
7293296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
7303296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
7313296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
7323296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.PRESENCE,
7333296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
734aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        addProjection(sDataProjectionMap, Data.CONTACT_CHAT_CAPABILITY,
735aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY);
7363296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS,
7373296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
7383296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_TIMESTAMP,
7393296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
7403296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_RES_PACKAGE,
7413296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
7423296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_LABEL,
7433296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
7443296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDataProjectionMap, Data.STATUS_ICON,
7453296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
7463296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
7475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        // Projection map for data grouped by contact (not raw contact) and some data field(s)
7485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap = new HashMap<String, String>();
7495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data._ID,
7505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                "MIN(" + Data._ID + ") AS " + Data._ID);
7515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA_VERSION, Data.DATA_VERSION);
7525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
7535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
7545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.RES_PACKAGE, Data.RES_PACKAGE);
7555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.MIMETYPE, Data.MIMETYPE);
7565e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA1, Data.DATA1);
7575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA2, Data.DATA2);
7585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA3, Data.DATA3);
7595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA4, Data.DATA4);
7605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA5, Data.DATA5);
7615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA6, Data.DATA6);
7625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA7, Data.DATA7);
7635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA8, Data.DATA8);
7645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA9, Data.DATA9);
7655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA10, Data.DATA10);
7665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA11, Data.DATA11);
7675e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA12, Data.DATA12);
7685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA13, Data.DATA13);
7695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA14, Data.DATA14);
7705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.DATA15, Data.DATA15);
7715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC1, Data.SYNC1);
7725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC2, Data.SYNC2);
7735e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC3, Data.SYNC3);
7745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Data.SYNC4, Data.SYNC4);
7755e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(RawContacts.CONTACT_ID, RawContacts.CONTACT_ID);
7768f1631f8a610e7278526916ce73ac1e422a5c9b8Jeff Sharkey        sDistinctDataProjectionMap.put(Contacts.LOOKUP_KEY, Contacts.LOOKUP_KEY);
7775e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME, Contacts.DISPLAY_NAME);
7785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME_ALTERNATIVE,
7795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE);
7805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.DISPLAY_NAME_SOURCE, Contacts.DISPLAY_NAME_SOURCE);
7815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHONETIC_NAME, Contacts.PHONETIC_NAME);
7825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHONETIC_NAME_STYLE, Contacts.PHONETIC_NAME_STYLE);
7835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SORT_KEY_PRIMARY, Contacts.SORT_KEY_PRIMARY);
7845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SORT_KEY_ALTERNATIVE,
7855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Contacts.SORT_KEY_ALTERNATIVE);
7865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.CUSTOM_RINGTONE, Contacts.CUSTOM_RINGTONE);
7875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.SEND_TO_VOICEMAIL, Contacts.SEND_TO_VOICEMAIL);
7885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.LAST_TIME_CONTACTED, Contacts.LAST_TIME_CONTACTED);
7895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.TIMES_CONTACTED, Contacts.TIMES_CONTACTED);
7905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.STARRED, Contacts.STARRED);
7915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.PHOTO_ID, Contacts.PHOTO_ID);
792a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov        sDistinctDataProjectionMap.put(Contacts.IN_VISIBLE_GROUP, Contacts.IN_VISIBLE_GROUP);
7935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sDistinctDataProjectionMap.put(GroupMembership.GROUP_SOURCE_ID,
7945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID);
7955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
7963296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Contacts-level statuses
7973296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_PRESENCE,
7983296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE);
799aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_CHAT_CAPABILITY,
800aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY);
8013296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS,
8023296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS);
8033296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_TIMESTAMP,
8043296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
8053296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_RES_PACKAGE,
8063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
8073296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_LABEL,
8083296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL);
8093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Contacts.CONTACT_STATUS_ICON,
8103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON);
8113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
8123296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Handle projections for Data-level statuses
8133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.PRESENCE,
8143296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Tables.PRESENCE + "." + StatusUpdates.PRESENCE);
815aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        addProjection(sDistinctDataProjectionMap, Data.CHAT_CAPABILITY,
816aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY);
8173296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS,
8183296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS);
8193296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_TIMESTAMP,
8203296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP);
8213296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_RES_PACKAGE,
8223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE);
8233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_LABEL,
8243296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_LABEL);
8253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        addProjection(sDistinctDataProjectionMap, Data.STATUS_ICON,
8263296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                StatusUpdatesColumns.CONCRETE_STATUS_ICON);
8273296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
828e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap = new HashMap<String, String>();
829e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup._ID,
830fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts._ID
831fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup._ID);
83256d2bd40ebb238c2990bc239f68c7e61c7d5b02cEvan Millar        sPhoneLookupProjectionMap.put(PhoneLookup.LOOKUP_KEY,
833fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.LOOKUP_KEY
834fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.LOOKUP_KEY);
835e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.DISPLAY_NAME,
836fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.DISPLAY_NAME
837fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.DISPLAY_NAME);
838e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LAST_TIME_CONTACTED,
839fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.LAST_TIME_CONTACTED
840e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.LAST_TIME_CONTACTED);
841e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TIMES_CONTACTED,
842fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.TIMES_CONTACTED
843fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.TIMES_CONTACTED);
844e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.STARRED,
845fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.STARRED
846fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.STARRED);
847e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.IN_VISIBLE_GROUP,
848fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.IN_VISIBLE_GROUP
849fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.IN_VISIBLE_GROUP);
850e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.PHOTO_ID,
851fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.PHOTO_ID
852fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.PHOTO_ID);
853e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.CUSTOM_RINGTONE,
854fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.CUSTOM_RINGTONE
855fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.CUSTOM_RINGTONE);
856e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.HAS_PHONE_NUMBER,
857fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.HAS_PHONE_NUMBER
858fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        + " AS " + PhoneLookup.HAS_PHONE_NUMBER);
859e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.SEND_TO_VOICEMAIL,
860fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                "contacts_view." + Contacts.SEND_TO_VOICEMAIL
861e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                        + " AS " + PhoneLookup.SEND_TO_VOICEMAIL);
862e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.NUMBER,
863e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.NUMBER + " AS " + PhoneLookup.NUMBER);
864e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.TYPE,
865e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.TYPE + " AS " + PhoneLookup.TYPE);
866e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov        sPhoneLookupProjectionMap.put(PhoneLookup.LABEL,
867e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Phone.LABEL + " AS " + PhoneLookup.LABEL);
8689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
869ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
870ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
87189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups._ID, Groups._ID);
872035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
873035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
8749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
8759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
8769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
87789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.RES_PACKAGE, Groups.RES_PACKAGE);
878ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
87967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        columns.put(Groups.TITLE_RES, Groups.TITLE_RES);
880ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
8813cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.SYSTEM_ID, Groups.SYSTEM_ID);
88294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        columns.put(Groups.DELETED, Groups.DELETED);
8833cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        columns.put(Groups.NOTES, Groups.NOTES);
88438446bf47c5ee2080df69f5fc8a33ad2fa3e61b5Jeff Sharkey        columns.put(Groups.SHOULD_SYNC, Groups.SHOULD_SYNC);
88589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC1, Groups.SYNC1);
88689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC2, Groups.SYNC2);
88789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC3, Groups.SYNC3);
88889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        columns.put(Groups.SYNC4, Groups.SYNC4);
889ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
890ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
8916cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // RawContacts and groups projection map
892ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
893ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
894d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
895d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
896ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
897ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
898ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
899d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + ContactsColumns.CONCRETE_ID + ") FROM "
900d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS + " WHERE "
901ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
902f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov                + " AND " + Contacts.HAS_PHONE_NUMBER + ") AS " + Groups.SUMMARY_WITH_PHONES);
903ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
904ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
905b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
906b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
907b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
908b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
9090c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID1, AggregationExceptions.RAW_CONTACT_ID1);
9100c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        columns.put(AggregationExceptions.RAW_CONTACT_ID2, AggregationExceptions.RAW_CONTACT_ID2);
911b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
912b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
913eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        // Settings projection map
914eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns = new HashMap<String, String>();
915eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_NAME, Settings.ACCOUNT_NAME);
916eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.ACCOUNT_TYPE, Settings.ACCOUNT_TYPE);
917eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.UNGROUPED_VISIBLE, Settings.UNGROUPED_VISIBLE);
918eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        columns.put(Settings.SHOULD_SYNC, Settings.SHOULD_SYNC);
919341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey        columns.put(Settings.ANY_UNSYNCED, "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
920341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + ",(SELECT (CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL THEN 1 ELSE MIN("
921341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Groups.SHOULD_SYNC + ") END) FROM " + Tables.GROUPS + " WHERE "
922fc4e892529eccdfa42121f0304ec7d0dbb42d6c9Dmitri Plotnikov                + GroupsColumns.CONCRETE_ACCOUNT_NAME + "=" + SettingsColumns.CONCRETE_ACCOUNT_NAME
923341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
924341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0 THEN 1 ELSE 0 END) AS "
925341e4621f2ee7614c66bc25dd3da70eaaa866b46Jeff Sharkey                + Settings.ANY_UNSYNCED);
92668936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_COUNT, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
92768936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " GROUP BY "
92868936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID + " HAVING " + Clauses.HAVING_NO_GROUPS
92968936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + ")) AS " + Settings.UNGROUPED_COUNT);
93068936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey        columns.put(Settings.UNGROUPED_WITH_PHONES, "(SELECT COUNT(*) FROM (SELECT 1 FROM "
931e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS + " WHERE "
93268936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Contacts.HAS_PHONE_NUMBER + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
93368936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + " HAVING " + Clauses.HAVING_NO_GROUPS + ")) AS "
93468936cefd4caa779169ea00ffd1adc399e634c9bJeff Sharkey                + Settings.UNGROUPED_WITH_PHONES);
935eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        sSettingsProjectionMap = columns;
936eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
937373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        columns = new HashMap<String, String>();
9384dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        columns.put(PresenceColumns.RAW_CONTACT_ID, PresenceColumns.RAW_CONTACT_ID);
9390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.DATA_ID,
9400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                DataColumns.CONCRETE_ID + " AS " + StatusUpdates.DATA_ID);
94182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_ACCOUNT, StatusUpdates.IM_ACCOUNT);
94282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.IM_HANDLE, StatusUpdates.IM_HANDLE);
94382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PROTOCOL, StatusUpdates.PROTOCOL);
94470c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // We cannot allow a null in the custom protocol field, because SQLite3 does not
94570c314fb9b2ef3d47340b93816d46200aba9f5ecDmitri Plotnikov        // properly enforce uniqueness of null values
94682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.CUSTOM_PROTOCOL, "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL
94782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + "='' THEN NULL ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END) AS "
94882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                + StatusUpdates.CUSTOM_PROTOCOL);
94982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        columns.put(StatusUpdates.PRESENCE, StatusUpdates.PRESENCE);
950aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        columns.put(StatusUpdates.CHAT_CAPABILITY, StatusUpdates.CHAT_CAPABILITY);
9510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS, StatusUpdates.STATUS);
9520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_TIMESTAMP, StatusUpdates.STATUS_TIMESTAMP);
9530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_RES_PACKAGE, StatusUpdates.STATUS_RES_PACKAGE);
9540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_ICON, StatusUpdates.STATUS_ICON);
9550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        columns.put(StatusUpdates.STATUS_LABEL, StatusUpdates.STATUS_LABEL);
95682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sStatusUpdatesProjectionMap = columns;
95719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
9581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // Live folder projection
9591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap = new HashMap<String, String>();
9601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders._ID,
9611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts._ID + " AS " + LiveFolders._ID);
9621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        sLiveFoldersProjectionMap.put(LiveFolders.NAME,
9631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                Contacts.DISPLAY_NAME + " AS " + LiveFolders.NAME);
9641b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // TODO: Put contact photo back when we have a way to display a default icon
9651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // for contacts without a photo
9661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP,
9671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        //      Photos.DATA + " AS " + LiveFolders.ICON_BITMAP);
9684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
9694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
9703296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    private static void addProjection(HashMap<String, String> map, String toField, String fromField) {
9713296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        map.put(toField, fromField + " AS " + toField);
9723296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey    }
9733296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
9743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
9753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
9763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
9773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
9783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
980653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
9813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9821129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        @SuppressWarnings("all")
9833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
9843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
985a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
986a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
987a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
988a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
989a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
990a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
991a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
992a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
9933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
995653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
996653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
997b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
998653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
999653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
1000653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1001653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
10023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
10033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
10043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
10055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1006e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
1007e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1008e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
1009e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
1010653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
1011e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1012e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1013e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
10143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
10173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
10183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
1019813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         *
1020813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * @return true if update changed something
10213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1022813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1023f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
102414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
102514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1026653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1027653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
1028653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
1029653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
1030653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
1031653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1032653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
1033653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
1034653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
1035653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
1036653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
1037653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1038653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
1039653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
1040653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1041653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1042653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
10434da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
10444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " =?", mSelectionArgs1);
1045653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1046653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1047f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
1048653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
1049653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1050813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1051813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
10523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
105514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
105614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
105714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
10584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
10594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=?", mSelectionArgs1);
10604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
10614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
10623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
10635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
10643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
10663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
10694da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            long mimeTypeId = getMimeTypeId();
1070e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
1071e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
10724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
10734da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            Cursor c = db.query(DataDeleteQuery.TABLE,
10744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    DataDeleteQuery.CONCRETE_COLUMNS,
10754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=?" +
10764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        " AND " + DataColumns.MIMETYPE_ID + "=" + mimeTypeId,
10774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
10783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
1079e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
108014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
1081f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
1082e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
1083e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
1084e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
1085e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
10863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
10873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
10883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
10893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            if (primaryId != -1) {
10914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                setIsPrimary(rawContactId, primaryId, mimeTypeId);
10924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            }
1093e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1094e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1095e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
1096e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
1097e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
1098e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
1099e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1100e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
11013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
110325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
1104285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1105d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                updateRawContactDisplayName(db, rawContactId);
1106fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(db, rawContactId);
1107285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
11083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1109a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1110622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1111622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
1112813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * as baseline, but augmented with any updates.  Returns null if there is
1113813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * no change.
1114622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1115622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
1116622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
1117813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            boolean changing = false;
1118622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
11194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
11204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=?",
11214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
1122622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
1123622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
1124622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
1125622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
1126813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        final String value = cursor.getString(i);
1127813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        if (!changing && update.containsKey(key)) {
1128813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            Object newValue = update.get(key);
1129813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            String newString = newValue == null ? null : newValue.toString();
1130813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            changing |= !TextUtils.equals(newString, value);
1131813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        }
1132813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        values.put(key, value);
1133622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
1134622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
1135622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
1136622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1137622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1138813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!changing) {
1139813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return null;
1140813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1141813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1142622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1143622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1144622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
11453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
11483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
11503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
11513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
11533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
1155622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
11563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1157622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
11583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1159622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
11603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
11635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1164622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
116514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
116614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
116714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1168f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1169d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            Integer fullNameStyle = values.getAsInteger(StructuredName.FULL_NAME_STYLE);
1170d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name,
117151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                    fullNameStyle != null
117251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            ? mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)
117351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            : FullNameStyle.UNDEFINED);
117448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId, values);
117525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1176813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
117714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
117814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
117914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
118014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1181813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1182f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1183622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1184622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1185cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1186622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1187813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {  // No change
1188813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1189813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1190813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1191622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
119214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1193f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
11947ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME) ||
11957ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_FAMILY_NAME) ||
11967ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME) ||
11977ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)) {
11987ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                augmented.putAll(values);
11997ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                String name = augmented.getAsString(StructuredName.DISPLAY_NAME);
1200f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
12017ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                Integer fullNameStyle = augmented.getAsInteger(StructuredName.FULL_NAME_STYLE);
1202d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name,
120351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                        fullNameStyle != null
120451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                ? mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)
120551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                : FullNameStyle.UNDEFINED);
12067ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                insertNameLookupForPhoneticName(rawContactId, dataId, augmented);
120714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
120825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1209813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
1210813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
121114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
121214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
121314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
121414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
121514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
121614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
121714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
121814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
121914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1220f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
122125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1222813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
122314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
12243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1227622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
12283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1229622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1230622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1231622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1232622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
12333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1234622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1235622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1236622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1237622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1238622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1239622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1240622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
124167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1242622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
124367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
124467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1245622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1246622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
12478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1248622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1249622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
125067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
125167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
125267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
125367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
125467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
125567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
12568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1257622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
12584cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                // As the name could be changed, let's guess the name style again.
12594cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                name.fullNameStyle = FullNameStyle.UNDEFINED;
12604cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                mSplitter.guessNameStyle(name);
1261ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                int unadjustedFullNameStyle = name.fullNameStyle;
1262ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                name.fullNameStyle = mSplitter.getAdjustedFullNameStyle(name.fullNameStyle);
12635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                final String joined = mSplitter.join(name, true);
1264622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
12655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
1266ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                update.put(StructuredName.FULL_NAME_STYLE, unadjustedFullNameStyle);
12675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
12684cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao            } else if (touchedUnstruct && touchedStruct){
1269d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.FULL_NAME_STYLE)) {
1270d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.FULL_NAME_STYLE,
1271d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessFullNameStyle(unstruct));
12724cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1273d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.PHONETIC_NAME_STYLE)) {
1274d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.PHONETIC_NAME_STYLE,
1275d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessPhoneticNameStyle(unstruct));
12764cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1277622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1278622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1279622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1280622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1281622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1282622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1283622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1284622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1285622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1286622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1287622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1288622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1289622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1290622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1291622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1292622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1293622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1294622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1295622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1296813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1297f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1298622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1299622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1300813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {    // No change
1301813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1302813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1303813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1304622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1305f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1306813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1307622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1308622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1309622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1310622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1311622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1312622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1313622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1314622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1315622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1316622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1317622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1318622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1319622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1320622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1321622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1322622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1323622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1324622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
132567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
132667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
132767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
132867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1329622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1330622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1331622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1332622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1333622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1334622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
133567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
133667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
133767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1338622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1339622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1340622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
13413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
13423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
13463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
13483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
13493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
13513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
13523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
13533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
13543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1358622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1359622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1360622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
13613cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1362622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1363813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1364f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1365622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1366622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1367813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {        // No change
1368813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1369813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1370622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1371813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return super.update(db, values, c, callerIsSyncAdapter);
1372622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
13733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1374622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1375622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1376622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1377622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1378622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1379622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1380622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1381622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
13823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1383622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1384622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1385622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1386622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1387622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
13883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
13923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
13943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
13953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1399a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1400a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1401a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1402a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1403a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
140425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1405a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1406a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
14073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
14093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
1410813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1411f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1412813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1413813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1414813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
141514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
141631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsCompany = values.containsKey(Organization.COMPANY);
141731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsTitle = values.containsKey(Organization.TITLE);
141831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            if (containsCompany || containsTitle) {
1419813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1420813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1421813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
142231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String company;
142331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
142431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsCompany) {
142531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = values.getAsString(Organization.COMPANY);
142631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
142731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
142831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = DatabaseUtils.stringForQuery(db,
142931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.COMPANY +
143031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
143131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
143231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
143331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
143431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String title;
143531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsTitle) {
143631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = values.getAsString(Organization.TITLE);
143731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
143831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
143931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = DatabaseUtils.stringForQuery(db,
144031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.TITLE +
144131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
144231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
144331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
144431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
144531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                deleteNameLookup(dataId);
144631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                insertNameLookupForOrganization(rawContactId, dataId, company, title);
144731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
144831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
144931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            }
1450813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
145114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
145214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
145314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
145414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1455a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
145614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
145714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
145814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
145925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1460a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
146114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
146214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
146314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
146414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
14653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
14663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
14673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
14683cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
14693cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
14703cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
14713cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
14723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14733cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
14743cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1475e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1476e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1477e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1478e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1479e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1480e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1481e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
14825ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1483813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String email = values.getAsString(Email.DATA);
148414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
148514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
148614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
148725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1488813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String address = insertNameLookupForEmail(rawContactId, dataId, email);
1489813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (address != null) {
1490813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1491813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
149214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
149314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
149414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
149514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1496813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1497f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1498813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1499813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1500813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
150114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1502b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Email.DATA)) {
1503813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1504813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1505813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1506b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String address = values.getAsString(Email.DATA);
1507b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                deleteNameLookup(dataId);
1508b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                insertNameLookupForEmail(rawContactId, dataId, address);
1509b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1510813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1511b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1512813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1513813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
151414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
151514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
151614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
151714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
151814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
151914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
152014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
152114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
152214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1523f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
152425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1525813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
152614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1527e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1528e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1529e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1530e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1531e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1532e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1533e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1534e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1535e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1536e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1537e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1538e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1539e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1540e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
154114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
154214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
154314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
154414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
154514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
154614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
154714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
154814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
154914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
155014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
155114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
155214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1553813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!TextUtils.isEmpty(nickname)) {
1554813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1555813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                insertNameLookupForNickname(rawContactId, dataId, nickname);
1556813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1557813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
155814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
155914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
156014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
156114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1562813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1563f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
156414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
156514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
156614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1567813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1568813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1569813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
157014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1571b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Nickname.NAME)) {
1572b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String nickname = values.getAsString(Nickname.NAME);
1573b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                deleteNameLookup(dataId);
1574b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                insertNameLookupForNickname(rawContactId, dataId, nickname);
1575b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1576813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1577b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1578813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1579813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
158014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
158114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
158214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
158314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
158414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
158514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
158614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
158714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
158814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1589f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
159025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1591813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
159214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
159314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
159414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
159514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
15963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
15973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
15993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
16003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
16013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
16023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
16035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
16040b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
16050b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
16060b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
1607813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number);
1608813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
16090b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1610653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
16110b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1612285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
161325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1614813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                if (normalizedNumber != null) {
1615813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                    triggerAggregation(rawContactId);
1616813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                }
16170b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
16180b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
16190b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1620653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1621653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1622653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1623653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1624813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1625f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1626813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String number = null;
1627813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String normalizedNumber = null;
16280b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1629813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                number = values.getAsString(Phone.NUMBER);
1630813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                normalizedNumber = computeNormalizedNumber(number);
1631813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1632813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1633653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1634813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1635813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1636813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1637653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1638813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1639813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1640813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
16410b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1642285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
164325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1644813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
16450b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1646813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
164714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
164814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
164914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
165014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
165114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
165214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
165314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
165414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
165514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
165614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1657285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
165825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1659813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
166014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1661653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1662653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1663813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        private String computeNormalizedNumber(String number) {
1664e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
1665e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1666e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1667e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1668653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return normalizedNumber;
1669653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1670e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1671653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1672653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                String number, String normalizedNumber) {
1673e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1674653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
16755ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1676653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1677e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
167836045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.MIN_MATCH,
167936045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                        PhoneNumberUtils.toCallerIDMinMatch(number));
168036045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov
1681653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1682653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else {
16834da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
16844da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=?", mSelectionArgs1);
1685e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
16863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
16873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
16883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
16893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
16903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
16913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
16923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
16933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
16943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
16953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
16963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
16973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
16983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
16993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
17003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
17013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
17023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
17033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1704653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1705653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1706653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1707653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1708653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1709653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1710653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1711653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1712653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
17130be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
17140be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
17150be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1716653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1717653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1718653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1719813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1720f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
172114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1722653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1723813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1724813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1725813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
17260be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
1727813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
17280be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
17290be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
17300be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
17310be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
17320be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
17330be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
17340be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
17350be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
17360be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
17370be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
17380be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
1739b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
17400be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
1741b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
17420be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1743653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1744653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1745653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1746653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1747653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1748653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1749653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1750653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1751653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1752653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1753653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1754653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1755653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1756653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1757653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1758653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1759653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1760653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1761653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1762653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1763653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1764653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1765653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1766653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1767ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
1768ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        mInsertedRawContacts.get(rawContactId));
1769653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1770653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1771653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1772653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1773653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1774653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1775a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1776a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1777a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1778a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1779a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1780a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1781a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1782a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1783a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1784285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1785285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1786285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1787a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1788a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1789a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1790a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1791813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1792f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1793a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1794813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1795813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1796813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1797813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1798a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1799813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1800a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1801a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1802a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1803a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1804a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1805a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1806a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1807a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1808a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1809a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1810a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1811ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    /**
1812ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1813ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1814ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1815ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    public class GroupIdCacheEntry {
1816ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1817ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1818ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1819ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1820ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1821a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
18223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1823b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
182431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
18254097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1826f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1827315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1828622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1829622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1830ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1831ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1832ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // is a list of groups with this group id.
1833ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1834ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
1835622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1836f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1837a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1838d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1839a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
184020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
18411129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private CharArrayBuffer mCharArrayBuffer = new CharArrayBuffer(128);
18425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private NameSplitter.Name mName = new NameSplitter.Name();
184373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
184420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
184509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
184609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
1847ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
184873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1849ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<Long, Account> mInsertedRawContacts = Maps.newHashMap();
1850b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1851a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private HashSet<Long> mDirtyRawContacts = Sets.newHashSet();
1852b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1853de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
18541a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
18551a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
185681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
185781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
18584cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
1859d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
186073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
18614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
18624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1863de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1864ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1865ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1866ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1867ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1868ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1869ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1870ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
187135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1872ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
1873de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
1874b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
1875a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1876b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1877767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
1878767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov                createPhotoPriorityResolver(context));
18790e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1880a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1881b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
1882653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
188351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
1884d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1885b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mSetPrimaryStatement = mDb.compileStatement(
1886653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1887653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1888653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1889653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1890653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1891b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mSetSuperPrimaryStatement = mDb.compileStatement(
1892653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1893653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1894653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1895653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1896653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1897653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1898653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1899653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1900653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1901653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1902653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1903b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mRawContactDisplayNameUpdate = mDb.compileStatement(
190425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
19055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                " SET " +
19065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_SOURCE + "=?," +
19075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_PRIMARY + "=?," +
19085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_ALTERNATIVE + "=?," +
19095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.PHONETIC_NAME + "=?," +
19105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.PHONETIC_NAME_STYLE + "=?," +
19115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.SORT_KEY_PRIMARY + "=?," +
19125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.SORT_KEY_ALTERNATIVE + "=?" +
191325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
19143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1915b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mLastStatusUpdate = mDb.compileStatement(
1916a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                "UPDATE " + Tables.CONTACTS +
1917a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1918a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "(SELECT " + DataColumns.CONCRETE_ID +
1919a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " FROM " + Tables.STATUS_UPDATES +
1920a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.DATA +
1921a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1922a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1923a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
1924a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1925a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1926a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
19270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
19280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                                + StatusUpdates.STATUS +
1929a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                        " LIMIT 1)" +
1930a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1931e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1932b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mNameLookupInsert = mDb.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1933f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1934f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1935f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1936b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mNameLookupDelete = mDb.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1937f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1938f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1939b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateInsert = mDb.compileStatement(
1940a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1941a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
19420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
19430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
19440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
19450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
19460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?)");
1947a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1948b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateReplace = mDb.compileStatement(
1949a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1950a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
19510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
19520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
19530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
19540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
19550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
19560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1957a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1958b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateAutoTimestamp = mDb.compileStatement(
1959a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
19600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
19610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
1962a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
19630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
19640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
1965b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusAttributionUpdate = mDb.compileStatement(
19660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
19670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
19680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
19690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
19700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1971a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1972b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateDelete = mDb.compileStatement(
1973a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1974a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1975a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1976f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        // When setting NAME_VERIFIED to 1 on a raw contact, reset it to 0
1977f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        // on all other raw contacts in the same aggregate
1978b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mResetNameVerifiedForOtherRawContacts = mDb.compileStatement(
1979f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
1980f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " SET " + RawContacts.NAME_VERIFIED + "=0" +
1981f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " WHERE " + RawContacts.CONTACT_ID + "=(" +
1982f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        "SELECT " + RawContacts.CONTACT_ID +
1983f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1984f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        " WHERE " + RawContacts._ID + "=?)" +
1985f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " AND " + RawContacts._ID + "!=?");
1986f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
1987f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1988f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
19891129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdStructuredName = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE);
19901129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdOrganization = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE);
19911129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdNickname = mDbHelper.getMimeTypeId(Nickname.CONTENT_ITEM_TYPE);
19921129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdPhone = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
199304b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov
1994bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        verifyAccounts();
1995bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
199665ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
199765ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov            importLegacyContactsAsync();
199880952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov        } else {
199980952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov            verifyLocale();
200065ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        }
200165ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
20022a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        if (isAggregationUpgradeNeeded()) {
20032a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            upgradeAggregationAlgorithm();
20042a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        }
20052a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
2006b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return (mDb != null);
20074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
20084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2009ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao    private void initDataRowHandlers() {
2010ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers = new HashMap<String, DataRowHandler>();
2011ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao
2012ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
2013ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
2014ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
2015ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
2016ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
2017ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
2018ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
2019ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
2020ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
2021ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new StructuredNameRowHandler(mNameSplitter));
2022ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
2023ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new StructuredPostalRowHandler(mPostalSplitter));
2024ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
2025ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
2026ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao    }
202751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
2028767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov     * Visible for testing.
2029767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov     */
2030767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
2031767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov        return new PhotoPriorityResolver(context);
2032767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    }
2033767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov
2034767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
203551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
203651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
203704b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
20384cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
203904b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
20404cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
20414cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
204251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
2043cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
2044ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao        initDataRowHandlers();
20454cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
20464cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
20474cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    @Override
204851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    public void onConfigurationChanged(Configuration newConfig) {
20494f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
20504f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
20514f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
20524f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
205351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
205451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        verifyLocale();
20554cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
205651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2057c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
2058c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
2059c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
2060c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
2061c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
206251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
206351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
206451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
206551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
206651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
206751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
206851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
206951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void verifyLocale() {
2070f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2071f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
2072f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
2073f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
2074f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
2075f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
207651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
207751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
207851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
207951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
208051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
208151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
208251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
208351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
208451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
208551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
208651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        AsyncTask<Integer, Void, Void> task = new AsyncTask<Integer, Void, Void>() {
208751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
208851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            int savedProviderStatus;
208951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
209051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
209151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected Void doInBackground(Integer... params) {
209251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                savedProviderStatus = params[0];
209351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, currentLocale);
209451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                return null;
209551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
209651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
209751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
209851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected void onPostExecute(Void result) {
20999516b6eef76b3504913f5efcadf603969946a3d0Brad Fitzpatrick                prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
210051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                setProviderStatus(savedProviderStatus);
2101f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2102f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // Recursive invocation, needed to cover the case where locale
2103f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // changes once and then changes again before the db upgrade is completed.
2104f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                verifyLocale();
210551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
210651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        };
210751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
210851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        task.execute(providerStatus);
210951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
211051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
211131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
2112de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
2113b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
2114b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
211531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
211631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
2117013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
2118013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
2119013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
2120013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
21215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
21225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
21235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
21245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
21255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
21263d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
2127b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
2128b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
21293d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
21303d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2131568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
2132568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
2133568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2134568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2135568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2136568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
2137568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
2138568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2139568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
2140bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
2141bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
2142bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch == null) {
2143bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            mAccessLatch = new CountDownLatch(1);
2144bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2145568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2146568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
2147568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
2148568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
214980952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                final SharedPreferences prefs =
215080952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                    PreferenceManager.getDefaultSharedPreferences(getContext());
215180952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, mCurrentLocale);
215280952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
215380952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov
2154bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                LegacyContactImporter importer = getLegacyContactImporter();
2155bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (importLegacyContacts(importer)) {
2156bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportSuccess();
2157bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2158bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportFailure();
2159568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
2160568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
2161568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
2162568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2163568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
2164568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2165568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2166bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2167bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
2168bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2169bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
2170bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2171bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
2172bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
2173bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2174b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
2175b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
2176b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
2177bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
2178bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch.countDown();
2179bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch = null;
2180bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
2181bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
2182bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2183bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2184bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
2185bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2186bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
2187bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
2188bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2189bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
2190bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2191bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
2192bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
2193bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
2194bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
2195bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
2196bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
2197bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
2198bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
2199bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
2200bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2201bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
2202bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2203bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
2204bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
22053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
22063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
22073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
2208568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
22090e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
22103d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
22113d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
2212bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
2213bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2214bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
2215bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
2216bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
2217bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
22183d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
22193d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
22203d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
2221bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
2222bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
22233d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
22243d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2225a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
2226a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
2227a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
2228a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
2229b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
2230a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
2231a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2232568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2233568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
2234568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
2235568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
2236568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
2237568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
2238568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2239568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
2240ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
2241ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
2242ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
2243ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
2244ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
2245ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
2246ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
2247ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
224881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
2249ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
2250ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
2251568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
2252568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2253568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2254568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2255568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
2256568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2257568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
2258568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2259568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2260568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2261568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
2262bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch != null) {
2263bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2264bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2265bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2266bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2267bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (match == PROVIDER_STATUS && isLegacyContactImportNeeded()) {
2268bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2269bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2270bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    importLegacyContactsAsync();
2271bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2272bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2273bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2274bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2275bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2276bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2277568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2278568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
2279568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2280568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2281568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2282568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
2283568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2284568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
2285568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2286568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2287568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2288568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2289568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
2290568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2291568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
2292568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2293568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
22944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2295285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
2296bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2297b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2298b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2299285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
23001ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
2301b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2302b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2303b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2304b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
2305285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
2306b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
2307df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
2308a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.clear();
2309285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2310285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2311285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2312285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
23131129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
2314bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2315b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2316b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2317285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
2318b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
23191ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
23201a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
23211a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
2322b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
23231a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
2324b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2325b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2326b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2327bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2328b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2329b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
23301129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
233108e42c9c153a60bf2e7c71dd40bf84bb5fc93555Dmitri Plotnikov        for (long rawContactId : mInsertedRawContacts.keySet()) {
2332d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            updateRawContactDisplayName(mDb, rawContactId);
2333d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
2334285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
2335b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2336a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        if (!mDirtyRawContacts.isEmpty()) {
2337a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2338a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2339a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mDirtyRawContacts);
2340a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2341a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2342a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2343a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2344b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
2345a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2346a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2347a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mUpdatedRawContacts);
2348a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2349a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2350b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2351b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2352b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
2353b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
23549d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
23559d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
23569d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
23579d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2358b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2359b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2360b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2361b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2362b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2363a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2364a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2365a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2366a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2367a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private void appendIds(StringBuilder sb, HashSet<Long> ids) {
2368b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2369a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2370b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2371a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2372a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2373285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2374285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2375285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2376cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
237781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
237881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
237981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
238081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
238181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
238281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
238381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2384cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2385568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
238651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
238751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mProviderStatus = status;
238851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.ProviderStatus.CONTENT_URI,
238951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                null, false);
239051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
239151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2392285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
2393ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return mInsertedRawContacts.containsKey(rawContactId);
2394285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2395285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
23963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
23973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
23983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
23993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
24003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
24013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
24023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
24033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
24043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
24054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2406de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2407bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
24081129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2409b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2410f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2411f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2412f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2413f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2414a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2415a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
241635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2417a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
241835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2419b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
242035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
242135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2422d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2423d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
24246bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
24256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
24266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
24275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2428f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertRawContact(uri, values);
2429f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2430a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2431a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2432a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24335ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
24345ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2435f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2436f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2437a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2438a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2439a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2440a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2441f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2442f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2443a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2444a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2445a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2446ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2447f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2448f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2449ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2450ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2451ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2452eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
24535aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
245443880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2455eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2456eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2457eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
245882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
245982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
24601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
24611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
24621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2463a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
246481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2465f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2466a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2467a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
24697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
24707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2472de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2473a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2474a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2475a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2476e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2477e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2478e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2479e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2480e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2481e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2482e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2483e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2484e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2485e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2486e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2487e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2488e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
24897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2490e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2491f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2492f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2493e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2494f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2495f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2496f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2497e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2498e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2499e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2500e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2501e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
2502fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2503fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2504e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2505e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2506e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2507e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2508e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2509e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2510e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2511e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2512e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2513e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2514e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2515e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
2516fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2517fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2518e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2519e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2520e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2521f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2522f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2523e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2524f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2525f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2526e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2527e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2528f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2529f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2530e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2531f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2532f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2533f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2534f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2535035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2536f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2537e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
25387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
25397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2541d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
25426bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
25436bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
25446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
25456bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2546d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2547de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
25486bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
25496bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
25506bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2551a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2552a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2553f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2554f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2555a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2556a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2557f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertRawContact(Uri uri, ContentValues values) {
2558f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2559f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2560f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2561f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2562e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
25637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25643d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
25653d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2566f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
25673d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
25683d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2569f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2570f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
2571f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2572f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2573f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2574f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2575285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2576285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2577e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mInsertedRawContacts.put(rawContactId, account);
2578f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2579023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2580a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2581a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2582a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2583a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2584a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2585a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2586a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2587a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2588f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2589a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2590de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2591de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
259267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2593de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
259420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2595de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2596de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2597de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2598b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2599de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2600de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2601508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2602de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2603de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2604de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2605de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2606de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
26074097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2608b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2609de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2610a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2611a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2612a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2613f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2614de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2615a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2616b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2617a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
26184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
26194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
26208e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
26218e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
26228e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
26238e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
26248e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2625b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
2626f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
26278e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
26288e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
26298e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
26308e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
263169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
2632f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
26338e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
26348e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
26358e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
2636b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
2637f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
26388e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
26398e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
26408e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2641f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
26428e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2643f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2644c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
26452a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId);
2646f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
26478e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2648f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2649f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2650f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2651a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
26525ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
26539261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
26549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
26555ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
26569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
26579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
26589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
26599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
26609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
2661ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
2662ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Account account) {
2663ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2664ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (account == null) {
26654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
2666ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
26674da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
2668ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            try {
2669ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                if (c.moveToFirst()) {
2670ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
2671ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
2672ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2673ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        account = new Account(accountName, accountType);
2674ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    }
26759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2676ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            } finally {
2677ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                c.close();
26789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
26799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2680ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
26819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
26829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
2683ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    + "has a sourceid the the contact must be associated with "
26849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
26859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
26869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2687ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
2688ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (entries == null) {
2689ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            entries = new ArrayList<GroupIdCacheEntry>(1);
2690ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            mGroupIdCache.put(sourceId, entries);
2691ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2692ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2693ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int count = entries.size();
2694ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        for (int i = 0; i < count; i++) {
2695ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            GroupIdCacheEntry entry = entries.get(i);
2696ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) {
2697ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                return entry.groupId;
2698ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            }
2699ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2700ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2701ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        GroupIdCacheEntry entry = new GroupIdCacheEntry();
2702ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountName = account.name;
2703ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountType = account.type;
2704ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.sourceId = sourceId;
2705ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entries.add(0, entry);
2706ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
27079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
27085ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
2709ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
27109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2711df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
27129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
2713ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (c.moveToFirst()) {
2714ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = c.getLong(0);
27159261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
27169261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2717df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2718df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
27199261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
27209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
27219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
27229261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
27239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
27249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2725ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = groupId;
27269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
27279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
27289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
27299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2730ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2731ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return entry.groupId;
27329261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
27339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2734d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private interface DisplayNameQuery {
27351129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        public static final String RAW_SQL =
27361129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                "SELECT "
27371129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + DataColumns.MIMETYPE_ID + ","
27381129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.IS_PRIMARY + ","
27391129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.DATA1 + ","
27405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA2 + ","
27415dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA3 + ","
27425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA4 + ","
27435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA5 + ","
27445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA6 + ","
27455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA7 + ","
27465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA8 + ","
27475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA9 + ","
27485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA10 + ","
27495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA11 +
27501129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " FROM " + Tables.DATA +
27511129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " WHERE " + Data.RAW_CONTACT_ID + "=?" +
27521129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        " AND (" + Data.DATA1 + " NOT NULL OR " +
27531129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                Organization.TITLE + " NOT NULL)";
2754d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2755d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int MIMETYPE = 0;
2756d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int IS_PRIMARY = 1;
27575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int DATA1 = 2;
27585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int GIVEN_NAME = 3;                         // data2
27595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int FAMILY_NAME = 4;                        // data3
27605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PREFIX = 5;                             // data4
27615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int TITLE = 5;                              // data4
27625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int MIDDLE_NAME = 6;                        // data5
27635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int SUFFIX = 7;                             // data6
27645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_GIVEN_NAME = 8;                // data7
27655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_MIDDLE_NAME = 9;               // data8
27665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int ORGANIZATION_PHONETIC_NAME = 9;         // data8
27675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_FAMILY_NAME = 10;              // data9
27685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int FULL_NAME_STYLE = 11;                   // data10
27695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int ORGANIZATION_PHONETIC_NAME_STYLE = 11;  // data10
27705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_NAME_STYLE = 12;               // data11
2771d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2772d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2773d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    /**
2774d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * Updates a raw contact display name based on data rows, e.g. structured name,
2775d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * organization, email etc.
2776d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     */
2777ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
2778bca1c8b44f99528fc123d5547723e44771e8e934Mike Lockwood        int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
27795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        NameSplitter.Name bestName = null;
27805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String bestDisplayName = null;
27815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String bestPhoneticName = null;
27825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        int bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
2783d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
27841129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(rawContactId);
27851129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        Cursor c = db.rawQuery(DisplayNameQuery.RAW_SQL, mSelectionArgs1);
2786d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        try {
2787d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            while (c.moveToNext()) {
27881129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                int mimeType = c.getInt(DisplayNameQuery.MIMETYPE);
27895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                int source = getDisplayNameSource(mimeType);
27905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (source < bestDisplayNameSource || source == DisplayNameSources.UNDEFINED) {
27915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    continue;
27925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                }
27931129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
27945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (source == bestDisplayNameSource && c.getInt(DisplayNameQuery.IS_PRIMARY) == 0) {
27955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    continue;
2796d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
27971129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
27985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (mimeType == mMimeTypeIdStructuredName) {
27995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    NameSplitter.Name name;
28005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (bestName != null) {
28015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name = new NameSplitter.Name();
28025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    } else {
28035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name = mName;
28045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name.clear();
28055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
28065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.prefix = c.getString(DisplayNameQuery.PREFIX);
28075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.givenNames = c.getString(DisplayNameQuery.GIVEN_NAME);
28085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.middleName = c.getString(DisplayNameQuery.MIDDLE_NAME);
28095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.familyName = c.getString(DisplayNameQuery.FAMILY_NAME);
28105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.suffix = c.getString(DisplayNameQuery.SUFFIX);
28115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.fullNameStyle = c.isNull(DisplayNameQuery.FULL_NAME_STYLE)
28125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            ? FullNameStyle.UNDEFINED
28135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            : c.getInt(DisplayNameQuery.FULL_NAME_STYLE);
28145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticFamilyName = c.getString(DisplayNameQuery.PHONETIC_FAMILY_NAME);
28155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticMiddleName = c.getString(DisplayNameQuery.PHONETIC_MIDDLE_NAME);
28165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticGivenName = c.getString(DisplayNameQuery.PHONETIC_GIVEN_NAME);
28175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticNameStyle = c.isNull(DisplayNameQuery.PHONETIC_NAME_STYLE)
28185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            ? PhoneticNameStyle.UNDEFINED
28195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            : c.getInt(DisplayNameQuery.PHONETIC_NAME_STYLE);
28205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (!name.isEmpty()) {
28215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayNameSource = source;
28225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestName = name;
28235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
28245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                } else if (mimeType == mMimeTypeIdOrganization) {
28255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    mCharArrayBuffer.sizeCopied = 0;
28265dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.DATA1, mCharArrayBuffer);
28275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (mCharArrayBuffer.sizeCopied != 0) {
2828d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                        bestDisplayNameSource = source;
28291129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
28301129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
28315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticName = c.getString(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME);
28325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticNameStyle =
28335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                c.isNull(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME_STYLE)
28345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                    ? PhoneticNameStyle.UNDEFINED
28355dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                    : c.getInt(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME_STYLE);
28365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    } else {
28375dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        c.copyStringToBuffer(DisplayNameQuery.TITLE, mCharArrayBuffer);
28385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        if (mCharArrayBuffer.sizeCopied != 0) {
28391129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayNameSource = source;
28401129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayName = new String(mCharArrayBuffer.data, 0,
28411129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                    mCharArrayBuffer.sizeCopied);
28425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            bestPhoneticName = null;
28435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
28441129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        }
2845d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                    }
28465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                } else {
28475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    // Display name is at DATA1 in all other types.
28485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    // This is ensured in the constructor.
28495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    mCharArrayBuffer.sizeCopied = 0;
28515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.DATA1, mCharArrayBuffer);
28525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (mCharArrayBuffer.sizeCopied != 0) {
28535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayNameSource = source;
28545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
28555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
28565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticName = null;
28575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
28585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
2859d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
2860d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            }
2861d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2862d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        } finally {
2863d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            c.close();
2864d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        }
2865d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
28665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String displayNamePrimary;
28675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String displayNameAlternative;
28685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String sortKeyPrimary = null;
28695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String sortKeyAlternative = null;
28705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        int displayNameStyle = FullNameStyle.UNDEFINED;
28715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (bestDisplayNameSource == DisplayNameSources.STRUCTURED_NAME) {
28735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNameStyle = bestName.fullNameStyle;
28745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.CJK
28755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    || displayNameStyle == FullNameStyle.UNDEFINED) {
28765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.getAdjustedFullNameStyle(displayNameStyle);
28775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bestName.fullNameStyle = displayNameStyle;
28785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
28795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNamePrimary = mNameSplitter.join(bestName, true);
28815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNameAlternative = mNameSplitter.join(bestName, false);
28825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            bestPhoneticName = mNameSplitter.joinPhoneticName(bestName);
28845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            bestPhoneticNameStyle = bestName.phoneticNameStyle;
28855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
28865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNamePrimary = displayNameAlternative = bestDisplayName;
28875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
28885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (bestPhoneticName != null) {
28905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyPrimary = sortKeyAlternative = bestPhoneticName;
28915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (bestPhoneticNameStyle == PhoneticNameStyle.UNDEFINED) {
28925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bestPhoneticNameStyle = mNameSplitter.guessPhoneticNameStyle(bestPhoneticName);
28935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
28945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
28955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.UNDEFINED) {
28965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.guessFullNameStyle(bestDisplayName);
28975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (displayNameStyle == FullNameStyle.UNDEFINED
28985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        || displayNameStyle == FullNameStyle.CJK) {
28995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    displayNameStyle = mNameSplitter.getAdjustedNameStyleBasedOnPhoneticNameStyle(
29005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            displayNameStyle, bestPhoneticNameStyle);
29015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                }
29025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.getAdjustedFullNameStyle(displayNameStyle);
29035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
2904ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao            if (displayNameStyle == FullNameStyle.CHINESE ||
2905ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                    displayNameStyle == FullNameStyle.CJK) {
29065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                sortKeyPrimary = sortKeyAlternative =
2907cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao                        ContactLocaleUtils.getIntance().getSortKey(
2908ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                                displayNamePrimary, displayNameStyle);
29095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
29105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
29115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (sortKeyPrimary == null) {
29135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyPrimary = displayNamePrimary;
29145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyAlternative = displayNameAlternative;
29155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
29165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        setDisplayName(rawContactId, bestDisplayNameSource, displayNamePrimary,
29185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameAlternative, bestPhoneticName, bestPhoneticNameStyle,
29195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                sortKeyPrimary, sortKeyAlternative);
2920d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2921d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
29221129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private int getDisplayNameSource(int mimeTypeId) {
29231129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        if (mimeTypeId == mMimeTypeIdStructuredName) {
29241129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.STRUCTURED_NAME;
29251129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdEmail) {
29261129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.EMAIL;
29271129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdPhone) {
29281129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.PHONE;
29291129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdOrganization) {
29301129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.ORGANIZATION;
29311129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdNickname) {
29321129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.NICKNAME;
29331129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else {
29341129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.UNDEFINED;
29351129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        }
29361129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    }
29371129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
29389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
293920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
294020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2941f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
294220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
294320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2944de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2945de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
294614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2947de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2948de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
294914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
295014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
2951a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
2952a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
2953f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
295488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
295588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
295620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
295720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
2958de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
295920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
296020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
296120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
296220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
296320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
296488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
296588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
296688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
296720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
2968f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
296988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
297088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
29714da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
29724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=?",
29734da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
2974f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
297520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
297620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
297720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
297820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
297920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
298014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
298120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
298220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
298320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
298420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
298520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
298620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
298720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
298820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
298920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
29907a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
299120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
299220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
299320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2994a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
2995813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return rowHandler.delete(mDb, c);
299620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
299720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
299820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
299920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
300020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
300120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
3002ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
3003ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
3004f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
3005f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
3006f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
3007f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
3008e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
3009ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3010ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
3011f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
301267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
3013f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
301467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
3015f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
3016ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3017f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
3018f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
301973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
302073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3021f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
3022ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3023f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
30241a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3025ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
3026ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3027ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
3028ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3029ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
30305aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
3031e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
30325aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
30331a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
30341a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3035e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
30361a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
3037e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
3038e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3039e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3040ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
304182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
30421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
304382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
304482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
30450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
30464dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
30474dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
30480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
304982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
30504dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
30514dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
30524dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
30534dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
30541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
30551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3056dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3057dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
305882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
3059f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
30602526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3061dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3062dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3063dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30642526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
30652526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
30661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3067dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3068dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
30700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
30710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
30720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3073dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3074dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3075dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
30762526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mMimeTypeIdIm);
3077dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
30782526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mMimeTypeIdEmail);
3079f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3080f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3081f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3082f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3083f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3084f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3085f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
30862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
30872526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
30882526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
30892526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
30902526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30912526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
30922526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
30932526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3094dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
30952526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
30962526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3097dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
30982526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
30992526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3100dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
31012526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
31022526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
31032526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
31042526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
31052526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
31062526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3107dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
31082526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
31092526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3110dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3111dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
31121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
311382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
31142526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
31152526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3116dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
311770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
3118f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
311970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
31201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
31211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
3122de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
31232526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
3124c03e723e7b07434a3e60454606bc18e2df4ee06bDmitri Plotnikov                    Contacts.IN_VISIBLE_GROUP + " DESC, " + Data.RAW_CONTACT_ID);
31251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
312667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
31275ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
3128e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
31291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
31301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
31311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
31321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
31331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
313431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
313531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
313631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
31371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
31381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
313982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3140a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3141a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3142a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3143a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3144a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3145a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3146a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
314782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3148a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3149a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
315082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
315182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
315282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
315382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
315482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3155a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
315682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
315782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3158aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3159aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
31601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3161a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
3162a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
3163a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3164e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
31650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
316682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
316782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
31680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
31690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
31700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
31710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
31720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
31730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
31740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
31750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
31760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
31770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
31780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
31790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3180a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
3181a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
3182a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
318382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
318482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
3185a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
3186a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
31875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindString(mStatusUpdateReplace, 3, status);
31885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindString(mStatusUpdateReplace, 4, resPackage);
31895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindLong(mStatusUpdateReplace, 5, iconResource);
31905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindLong(mStatusUpdateReplace, 6, labelResource);
3191a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
3192a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
3193a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3194a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
3195a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
31965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateInsert, 2, status);
31975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateInsert, 3, resPackage);
31985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusUpdateInsert, 4, iconResource);
31995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusUpdateInsert, 5, labelResource);
3200a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
3201a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
3202a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
32030a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    long timestamp = System.currentTimeMillis();
3204a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
32055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateAutoTimestamp, 2, status);
3206a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
32075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateAutoTimestamp, 4, status);
3208a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
32090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
32105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusAttributionUpdate, 1, resPackage);
32115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusAttributionUpdate, 2, iconResource);
32125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusAttributionUpdate, 3, labelResource);
32130a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
32140a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.execute();
3215a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
3216e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3217e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3218bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3219a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
3220a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
3221a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
3222a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.execute();
3223a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3224a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3225a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
32261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
32271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
32284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3229de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3230bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3231b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3232b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3233b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3234f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3235f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3236508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3237508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
323835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3239b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
324035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3241b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
3242b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3243b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3244b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3245b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
3246b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3247cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3248cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3249cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3250cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3251cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3252d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3253d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3254cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return deleteContact(contactId);
32556bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
32566bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
32579fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
32582e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
32592e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
32602e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3261fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3262fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
32632e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
32642e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
32652e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
32662e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                return deleteContact(contactId);
32672e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
32682e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
32699fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
32709fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
32719fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                long contactId = ContentUris.parseId(uri);
32729fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
32739fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
32749fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
32759fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
32769fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
32779fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
32789fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
32799fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
32809fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
32819fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
32829fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
32839fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
328460de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
32859fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
32869fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
32879fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
32889fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
32899fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
32909fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
32919fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return deleteContact(contactId);
32929fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
32939fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
32949fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
32959fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
32969fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
32979fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
32989fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
32999fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
33009fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
33019fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
33022971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
33032971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
3304fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
3305fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3306e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
33072971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
33082971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
33092971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3310fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3311fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3312fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
33132971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
33142971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
33152971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
33162971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
33172971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
33182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
33192971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
33205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
33212971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
3322fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
3323fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3324508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3325508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
332620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3327f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3328944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3329f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
333020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
333120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
333248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
333348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
333448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
333548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3336508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3337f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
33384da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
33394da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3340ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3341ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3342ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3343f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
33445aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
33452971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
33462971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
33472971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
33482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
33492971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
3350e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
33512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
33522971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
33535aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
33542971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
33552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
33562971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
33572971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
335881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3359f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
336081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
33612971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3362508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3363508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3364eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
336543880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3366e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3367eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3368eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
336982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
33700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
33711f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
33721f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
337381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
337481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
33753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
337681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3377508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
33784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
33794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
33801c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3381ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3382b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
338394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
3384de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
338594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
338694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
338794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
338894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3389f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
3390de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
339194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
339294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
339394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3394f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
3395de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
339694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
339794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
33981a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
339994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
340094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
340194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
34025aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
3403e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
34041a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3405e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3406e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3407e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3408cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int deleteContact(long contactId) {
340996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
3410cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
341196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
341296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3413cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3414cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3415cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3416cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                markRawContactAsDeleted(rawContactId);
3417cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3418cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3419cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3420cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3421cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3422cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3423cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3424cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3425fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
34263389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
3427f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
342814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
3429fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
3430fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
3431fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
343233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
3433b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
3434cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            return markRawContactAsDeleted(rawContactId);
343533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
343633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
343733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
34380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
34399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
34409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
34419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
34429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
34439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
34449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
34459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
34469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
34470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
34480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3449cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    private int markRawContactAsDeleted(long rawContactId) {
345081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
345181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3452cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3453cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3454cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3455cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3456cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3457cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3458cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return updateRawContact(rawContactId, mValues);
3459cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3460cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
34614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3462de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3463de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3464bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3465b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3466b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3467b5a4add17815167d20a90645779df34cdf45280dFred Quintana
346835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
346900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
347000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3471b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3472b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
34731129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3474b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
3475b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3476b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3477b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3478f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3479f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
348000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
348135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3482b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3483b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3484b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3485b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3486b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3487b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3488b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3489b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3490b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3491b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3492b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
349335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3494d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
34958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(values, selection, selectionArgs);
349600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
349700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
349800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3499d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
35008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(ContentUris.parseId(uri), values);
3501c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3502c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3503c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
35042e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
35052e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
35062e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
35072e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
35082e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3509fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3510fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
35112e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
35122e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
35132e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
35148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count = updateContactOptions(contactId, values);
35152e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
35162e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
35172e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
35187d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
35197d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
35207d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
35217d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
35227d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
35237d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
35247d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
35257d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
35267d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
35277d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
352820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3529944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3530f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
353181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3532f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
353381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
353420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
353520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3536c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
353748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
353848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
353948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
354048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3541f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
354281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3543f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
354481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
354500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
354600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
35477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
35485ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
35495ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
35504529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count = updateRawContacts(values, selection, selectionArgs);
35517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
35527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
35537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
35545ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
355533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
35564529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
35574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
35584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
35594529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                                    + " AND(" + selection + ")", selectionArgs);
35604529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
35614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
35624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1);
35634529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
35647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
35657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
35667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3567ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
35685aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3569f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
357081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3571f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
357281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3573ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3574ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3575ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3576ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3577ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
35784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
35794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
358073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
35815aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
35825aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
358381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3584f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
358581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3586ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3587ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3588ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3589127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3590de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3591b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3592b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3593b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3594eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3595e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3596e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
359743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3598eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3599eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3600eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
36019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
36029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
36039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
36049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
36059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
360681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
360781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3608f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
360981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
361000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
361100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
361200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
36134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
36144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
36159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
36169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
36179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
36189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
36199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
36209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
36219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
36229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
36239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
36249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
36259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
36269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
36279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
36299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
36309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
36319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
36329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
36339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
36349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
36359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
36369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
36379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
36409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
36419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
36429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
36439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
36449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
36459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
36469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
36479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
36489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
36519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
36529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
36539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
36549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
36559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
36569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
36579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
36589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
36599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
36609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
36619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
36629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
36639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
36669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
36679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
36689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3669aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3670aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
36719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
36729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
36739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
36745aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3675f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
367673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3677ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3678ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
367973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3680f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
368173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
368273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
368373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
368473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
368573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
368673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
368773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
368873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3689ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
36901a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
36911a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
369294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
36936ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
36941129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
36956ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3696e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
36976ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
36986ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
36996ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
37006ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
37016ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
37026ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
37036ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
37046ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
37056ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3706ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
37076ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
37086ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
37096ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
37106ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
37116ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
37126ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
37136ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
37146ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
371594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
371694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
371794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3718b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3719b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3720e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
37211a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
37221a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3723e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3724e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3725e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3726e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
37274529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs) {
37284529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
37294529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
37304529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
37314529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
373273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
37334529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3734b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
373551bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
37364529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
37374529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
37384529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
37394529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
37404529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                updateRawContact(rawContactId, values);
37414529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
37424529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
37434529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
37444529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
37454529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
37464529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
37474529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
37484529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
37494529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
37504529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    private int updateRawContact(long rawContactId, ContentValues values) {
375196b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
375296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
375319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
375419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
375519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3756ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3757ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
375819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
375919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
376096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
376119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
376219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
376319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3764ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3765ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
376619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
376719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
376819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
376919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
377019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
377119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
377219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3773f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
377496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
37755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3776f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3777f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3778f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3779f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3780f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3781f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
378269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
3783f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3784f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3785433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
37864529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3787433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3788285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
37892b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3790285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3791f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3792f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3793f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3794f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3795f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
3796f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.bindLong(1, rawContactId);
3797f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.bindLong(2, rawContactId);
3798f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.execute();
3799f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3800f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3801f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
380219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
380319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                // undo delete, needs aggregation again.
3804ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                mInsertedRawContacts.put(rawContactId, new Account(accountName, accountType));
380519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
38065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
38075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
380833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
380933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3810321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3811f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
381220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
381320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
381420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
38155ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
381620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
381720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
381820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
381920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
382020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3821b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
382220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
382320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
382470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
382570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
382620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
382720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
382820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
382970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
383020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
383170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
383220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
383370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
383420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
383570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
383620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
383720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3838653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
383920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3840653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3841653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
384214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3843653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3844653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
3845f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
384620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3847653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3848653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
384920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
385020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3851653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
385220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
385320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3854f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3855653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3856653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3857321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3858653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
385914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
3860a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3861813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        if (rowHandler.update(mDb, values, c, callerIsSyncAdapter)) {
3862813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
3863813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
3864813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
3865a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
3866321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3867321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
38688c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
38698c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            String[] selectionArgs) {
38708c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3871b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
38728c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
38738c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
38748c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
38758c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
38768c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
38778c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                updateContactOptions(contactId, values);
38788c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
38798c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
38808c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
38818c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
38828c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
38838c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
38848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
38858c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
38868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
38878c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(long contactId, ContentValues values) {
3888d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
38898c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3890b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
3891d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3892b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
3893d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3894b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
3895d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3896b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
3897d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3898b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
3899d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
3900d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3901d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
39028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
3903d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
3904d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
3905d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
39068c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
3907c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
39088c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
3909c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
3910c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
39114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
39124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?", mSelectionArgs1);
39138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
39148c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
39158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
39168c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
3917b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
39188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
3919b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
39208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
3921b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
39228c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
3923b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
39248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
3925b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
39268c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
39278c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
39289b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
39296e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
39309b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
39319b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
39329b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
39339b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
39349b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
39359b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
3936f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
3937d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
3938127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
3939127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
39400c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
39410c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
394280c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
39430c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
39440c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
39450c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
39460c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
39470c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
39480c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
39490c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
3950b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
3951127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
39520c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
39534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
39544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
39550c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
39564da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
39574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
39580c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
39596bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
39606bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
39610c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
39620c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
39630c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
39640c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
3965127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
3966127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
39673389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
396869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
396969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
397069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
397169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
3972dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
39732a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1);
39742a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2);
3975127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
3976127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
3977127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
3978127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
3979b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
3980b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3981f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao    /**
3982f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao     * Check whether GOOGLE_MY_CONTACTS_GROUP exists, otherwise create it.
3983f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao     *
3984f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao     * @return the group id
3985f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao     */
3986f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao    private long getOrCreateMyContactsGroupInTransaction(String accountName, String accountType) {
3987f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        Cursor cursor = mDb.query(Tables.GROUPS, new String[] {"_id"},
3988f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                Groups.ACCOUNT_NAME + " =? AND " + Groups.ACCOUNT_TYPE + " =? AND "
3989f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                    + Groups.TITLE + " =?",
3990f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                new String[] {accountName, accountType, GOOGLE_MY_CONTACTS_GROUP_TITLE},
3991f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                null, null, null);
3992f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        try {
3993f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao            if(cursor.moveToNext()) {
3994f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                return cursor.getLong(0);
3995f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao            }
3996f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        } finally {
3997f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao            cursor.close();
3998f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        }
3999f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao
4000f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        ContentValues values = new ContentValues();
4001f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        values.put(Groups.TITLE, GOOGLE_MY_CONTACTS_GROUP_TITLE);
4002f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        values.put(Groups.ACCOUNT_NAME, accountName);
4003f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        values.put(Groups.ACCOUNT_TYPE, accountType);
4004f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        values.put(Groups.GROUP_VISIBLE, "1");
4005f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        return mDb.insert(Tables.GROUPS, null, values);
4006f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao    }
4007f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao
400870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4009f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4010627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
4011627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        boolean hasUnassignedContacts[] = new boolean[]{false};
401270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
401370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
4014743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            findValidAccounts(existingAccounts, hasUnassignedContacts);
4015743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
4016743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
4017743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
4018743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
4019743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
4020743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
4021743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
4022743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4023743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
402448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4025627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
4026743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
4027627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
4028627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
4029627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
403070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
403170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
403270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            for (Account account : accountsToDelete) {
40335f9e610be459bd1332e182c41cf7510265d5f7ccFred Quintana                Log.d(TAG, "removing data for removed account " + account);
4034627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                String[] params = new String[] {account.name, account.type};
4035627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
4036627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.GROUPS +
4037627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
4038627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
4039627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
4040627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.PRESENCE +
4041627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4042627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                "SELECT " + RawContacts._ID +
4043627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
4044627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4045627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
4046627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
4047627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.RAW_CONTACTS +
4048627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4049627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
4050627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                mDb.execSQL(
4051627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        "DELETE FROM " + Tables.SETTINGS +
4052627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4053627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
4054743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                mDb.execSQL(
4055743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                        "DELETE FROM " + Tables.ACCOUNTS +
4056743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                        " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
4057743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                        " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
4058627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4059627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
406033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
406133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
406233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
406333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // names of photos.  If so, fix up those contacts.
406433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
406533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
406633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
406733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
406869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
406969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
407069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
407133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
407233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
407369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
407469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
407533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
407633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
407733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
407833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
407933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
408033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
408133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
408233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
408333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
408433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
408533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
408633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
408733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
4088627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            if (hasUnassignedContacts[0]) {
4089627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4090627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                Account primaryAccount = null;
4091627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                for (Account account : accounts) {
409273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    if (isWritableAccount(account.type)) {
4093627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        primaryAccount = account;
4094627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                        break;
4095627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    }
4096627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4097627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4098627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (primaryAccount != null) {
4099627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    String[] params = new String[] {primaryAccount.name, primaryAccount.type};
4100f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                    if (primaryAccount.type.equals(DEFAULT_ACCOUNT_TYPE)) {
4101f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                        long groupId = getOrCreateMyContactsGroupInTransaction(
4102f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                                primaryAccount.name, primaryAccount.type);
4103f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                        if (groupId != -1) {
4104f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                            long mimeTypeId = mDbHelper.getMimeTypeId(
4105f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                                    GroupMembership.CONTENT_ITEM_TYPE);
4106f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                            mDb.execSQL(
4107f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                                    "INSERT INTO " + Tables.DATA + "(" + DataColumns.MIMETYPE_ID +
4108f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                                        ", " + Data.RAW_CONTACT_ID + ", "
4109f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                                        + GroupMembership.GROUP_ROW_ID + ") " +
41106dddb9abd6f738e43b41a4687c318533085ff0c7Dmitri Plotnikov                                    "SELECT " + mimeTypeId + ", "
41116dddb9abd6f738e43b41a4687c318533085ff0c7Dmitri Plotnikov                                            + RawContacts._ID + ", " + groupId +
4112f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                                    " FROM " + Tables.RAW_CONTACTS +
4113f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                                    " WHERE " + RawContacts.ACCOUNT_NAME + " IS NULL" +
41146dddb9abd6f738e43b41a4687c318533085ff0c7Dmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL"
4115f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                            );
4116f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                        }
4117f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao                    }
4118627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
4119627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.RAW_CONTACTS +
4120627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + RawContacts.ACCOUNT_NAME + "=?,"
4121627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + RawContacts.ACCOUNT_TYPE + "=?" +
4122627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " IS NULL" +
4123627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL", params);
4124627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4125627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // We don't currently support groups for unsynced accounts, so this is for
4126627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    // the future
4127627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    mDb.execSQL(
4128627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            "UPDATE " + Tables.GROUPS +
4129627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " SET " + Groups.ACCOUNT_NAME + "=?,"
4130627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                                    + Groups.ACCOUNT_TYPE + "=?" +
4131627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " IS NULL" +
4132627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                            " AND " + Groups.ACCOUNT_TYPE + " IS NULL", params);
4133743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
4134743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL(
4135743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4136743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " IS NULL" +
4137743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL");
4138627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
413970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
4140627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4141609cc5b4c96a6981f675a70ff743ecfec2b448e6Daniel Lehmann            mDbHelper.updateAllVisible();
4142609cc5b4c96a6981f675a70ff743ecfec2b448e6Daniel Lehmann
4143b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
414470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
414570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
414670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
414770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
414873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
414970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4150619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4151619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
4152627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
4153627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
4154743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov    private void findValidAccounts(Set<Account> validAccounts, boolean[] hasUnassignedContacts) {
4155743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
4156743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
4157743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
4158627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4159627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
4160627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (c.isNull(0) && c.isNull(1)) {
4161627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    hasUnassignedContacts[0] = true;
4162627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                } else {
4163627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
4164627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4165627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4166627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4167627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4168627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
4169627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4170627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4171627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    /**
4172622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
4173622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
417467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
417567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
417667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
417767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
417867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
417967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
418067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
418167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
418267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
418367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
418467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
418567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
4186dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
4187622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
4188dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
4189dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
4190622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
4191622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
4192dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
4193622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
4194622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
41954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
41964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
41974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
4198bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4199bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4200bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
42010b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4202b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
420335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4204d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
42051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4206c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4207c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4208619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
4209619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
4210a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
42114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
421235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4213b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
421435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
421535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4216d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4217763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4218619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4219619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4220619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4221d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
42224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4223763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
42244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
42254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
42266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
42276bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
42286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
42295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
42305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
42315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
42325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
42335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4234fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4235fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
42365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
42375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
42385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
42392d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                    // TODO: pull this out into a method and generalize to not require contactId
42405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
42415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4242763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
42434da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    String[] args;
42444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    if (selectionArgs == null) {
42454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        args = new String[2];
42464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    } else {
42474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        args = new String[selectionArgs.length + 2];
42484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
42494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    }
42504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    args[0] = String.valueOf(contactId);
425160de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                    args[1] = Uri.encode(lookupKey);
42524da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
42534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
42545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            groupBy, limit);
42555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (c.getCount() != 0) {
42565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
42575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
42585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
42595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    c.close();
42605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
42615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4262763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
42634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
42644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
42654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
42665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
42675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
42685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4269f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4270f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
427142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
4272763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
4273f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
42744da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
42754da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
42764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4277f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4278f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4279f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
428042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
428142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
428242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
428342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
428442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
428542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
428642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
428742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
428842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
428942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4290ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4291916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4292ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4293916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4294ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
4295916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(qb, uri, projection, filterParam);
4296ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4297ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4298ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4299ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4300ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
43014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
4302ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
4303d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
43044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
43054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4306e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
43075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
43084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
43094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
43104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4311763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4312ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
43135e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
43145e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
43155e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
43165e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    starredProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
43175e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                    frequentProjection = appendProjectionArg(projection, TIMES_CONTACED_SORT_COLUMN);
43185e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
43195e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
43204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
43214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
43224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4323d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
43245e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
43255e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
43264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4327d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4328d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
4329d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
4330763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
43314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
43324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4333d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
43345e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
43355e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
4336d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
4337d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
43384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4339d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4340d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
4341d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
4342d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
43434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
43444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
4345d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
4346d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4347d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
4348d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
4349d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4350d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4351ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4352763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4353b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
435471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
43554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4356b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4357b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4358b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4359b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
4360d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_DATA: {
43614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
436282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
43634da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
43644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
43656bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
43666bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
436700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4368ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_PHOTO: {
43693653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
437082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
43714da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
43724da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
43733653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
43743653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
43753653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
43763653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
43774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
437882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
437989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
43802815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
43812815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
43822815f58f72f109790585931f601a63ddc02536a5Evan Millar
438348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
438482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
43854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
438648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
43874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
438848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
438948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
439048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4391ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
439282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
439389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
4394ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
43954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
43964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4397a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
43985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
439945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
44005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
44015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
44025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
44035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
44047318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
44055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
440645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
44075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
44085e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
44095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (isPhoneNumber(filterParam)) {
44105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
44115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
44125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
44135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
44145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
44155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
44165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
44175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " FROM " + Tables.PHONE_LOOKUP
44185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
44195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(reversed);
44205e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append("')");
442145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
442245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
442345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
442445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
442545d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
442645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
442745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
44285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
44295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4430a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
4431ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
44325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
4433a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4434a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
4435a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
4436ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4437ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4438ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
44394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
444082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
444189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
44424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
44434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
44444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
444548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
444682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
44484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
44494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
445048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
445148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
445248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
44535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
445482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
445589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
44564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
445708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
445808768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
445908768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
446008768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
44614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
4462ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4463ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4464ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
44655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
446682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
446707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
446807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
446907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
447007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
447107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
447207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
447307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
44745e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
447507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
447607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
447707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
447807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
447907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
448007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
448107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
448207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
448307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
448407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
448507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " AND " + Data.DATA1 + " LIKE ");
448607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
448720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
448820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
448920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
449007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
449107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            /*
449207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * Using a UNION instead of an "OR" to make SQLite use the right
449307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * indexes. We need it to use the (mimetype,data1) index for the
449407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * email lookup (see above), but not for the name lookup.
449507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * SQLite is not smart enough to use the index on one side of an OR
449607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * but not on the other. Using two separate nested queries
449707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * and a UNION between them does the job.
449807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             */
449907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            sb.append(
450007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " UNION SELECT " + Data._ID +
450107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " FROM " + Tables.DATA +
450207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " WHERE +" + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
450307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " AND " + Data.RAW_CONTACT_ID + " IN ");
45047318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
450520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
45065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
45075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4508a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
45095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
45105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
4511a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4512a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
4513a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
45145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
45155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
45165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4517ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
451882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
451989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
452089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4521ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4522ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4523ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
452448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
452582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
452748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
452848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
45294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
453048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
453148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
453248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
45335ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4534763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
45354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
45364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
45374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
45385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
45395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
4540763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
45414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
45424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
45434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
45444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
45454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
45465ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
45475ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
454882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
45504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
4551e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4552e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4553e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
4554e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
455582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
4556e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4557e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4558e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
45594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
456082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
45624da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
4563a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
4564a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
4565a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
4566a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
45674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4568a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
4569a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
4570a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
4571e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
4572a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
4573a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4574e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
4575b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, number);
4576e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
4577e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov
4578e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
4579e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
4580e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
4581a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
4582a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
4583a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4584ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
4585b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4586ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
458789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4588ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4589ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4590ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4591ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
4592b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4593ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
45944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
45954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
4596ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4597ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4598ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4599ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
4600b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
4601ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
460289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
460389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
4604ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4605ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4606ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4607b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
46080c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
4609b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
4610b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
4611b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
4612b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
461331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
4614d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
46152d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
46162d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
46172d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
46182d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
461931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
4620d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
4621d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
462231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
462331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
462431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
462531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4626763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
46277581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
46287581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
46292d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        maxSuggestions, filter);
463031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
463131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4632eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
4633eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
4634eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
463589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4636e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4637e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
4638e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
4639b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
4640e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
464182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4642b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
4643e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4644e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
464582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4646b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
4647e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4648e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
4649e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4650eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
4651eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
4652eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
465382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
46540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
46555ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
46565ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
46575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
465882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
46590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
46604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
46614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
46625ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
46635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
46645ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
4665c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
4666a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
4667c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4668c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4669c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
46702d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
46712d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, lookupKey, projection);
4672c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4673c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
46741b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
4675b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
46761b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
46771b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
46781b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
46791b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
4680b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
46811b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
46821b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
46831b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
46841b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
46851b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
4686b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
46871b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
46881b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
46891b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
46901b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
46911b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
4692b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
46931b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
469471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
46951b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
46961b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
46971b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
469846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
469946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
470046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
470146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
470246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
470346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
470446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
470546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                setTablesAndProjectionMapForRawContactsEntities(qb, uri);
47064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
47074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
470846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
470946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
471046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
471109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
471209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
471309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
471409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
47154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
4716f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
4717c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
47184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
47194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
47207f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov        qb.setStrictProjectionMap(true);
47217f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
4722ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
4723ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
4724ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
4725ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
4726ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4727ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
47285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
47295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
47305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
47315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
47325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
4733038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
4734038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
4735038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
4736038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
47375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
47385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
47394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
47404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
47414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
47424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
47434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
47444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
474509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
474609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
474709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
474809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
474909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
475009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
475109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
475209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
475309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
475409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
475509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
475609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
475709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
475809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
475909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
476009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
476109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4762bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
4763bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
4764bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
4765bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
4766ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4767bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4768bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
4769ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
4770ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4771bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
4772bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
4773bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
4774bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4775de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
4776ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4777ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4778ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
4779ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
4780ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
4781ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
4782ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
4783ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
4784ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
4785ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4786ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
4787ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
4788ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
4789ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
4790ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
4791ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
4792ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
4793ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
4794ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
4795ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
4796ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
4797ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4798ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
4799ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
4800ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4801ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4802bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
4803ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
4804bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
4805bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "SUBSTR(" + sortKey + ",1,1) AS " + AddressBookIndexQuery.LETTER);
4806bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4807bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
4808bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
4809bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
4810bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
4811bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
4812bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
4813bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
4814ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
4815bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "GET_PHONEBOOK_INDEX(SUBSTR(" + sortKey + ",1,1),'" + locale + "')"
4816bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
4817ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
4818ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
4819ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
4820ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4821f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
4822ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
4823ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
4824ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4825ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
4826f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
4827ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
4828ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
4829bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
4830bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
4831bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4832bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
4833bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
4834bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
4835ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
4836f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
4837bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
4838bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
4839bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
4840bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
4841bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
4842bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
4843bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
4844bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
4845bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
4846bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
4847bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4848bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
4849bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
4850bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
4851bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
4852bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
4853bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
4854bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
4855bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
4856ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
4857ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4858ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            final Bundle bundle = new Bundle();
4859ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
4860f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
4861ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            return new CursorWrapper(cursor) {
4862ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
4863ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                @Override
4864ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                public Bundle getExtras() {
4865ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                    return bundle;
4866ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                }
4867ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            };
4868ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
4869f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
4870ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4871ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
4872ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
48732d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
487492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
487592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
487692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
487792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
48782d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
48792d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
48805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
48815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
48825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
488392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
488492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
488592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
488692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
488792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
488892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
488992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
489092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
489192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
489292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
489392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
489492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
489592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
489692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
489792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
489892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
489992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
490092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
490192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
49025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
49035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
49045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
49065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
49075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
49095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
49105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
49125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
49135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
49145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
49155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
49165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
49175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
49195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
49205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
49215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
49225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
49235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
49255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
49265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
49275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
49285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
49295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
493092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
49315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
49325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
49335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
49345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
49355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
49365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
49375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
49395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
49405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
49415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
49425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
49435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
49445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
49455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
49465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
49475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
49485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
494992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
495092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
49515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
49525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
49535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
49545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
49555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
49565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
49575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
49585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
49595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
49605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
49625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
49635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
496492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
496592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
49665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
49685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
49695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
49705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
497192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
49725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
49735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
49755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
49765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
497792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
49785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
49795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
498092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
498192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
498292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
498392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
49845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
49855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
498692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
498792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
498892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
49895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
49905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
499192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
499292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
49935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
499492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
499592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
499692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
499792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
499892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
499992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
500092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
500192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
500292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
500392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
500492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
500592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
500692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
500792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
500892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
500992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
501092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
501192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
501292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
501392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
501492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
50155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
50165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
501792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
501892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
501992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
502092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
502192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
502292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
502392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
502492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
502592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
502692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
502792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
502892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
502992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
503092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
503192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
503292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
503392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
503492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
503592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
503692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
503792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
50385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
50395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
50405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
50415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
504292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
504392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
50445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
50455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
50465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
50475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
50485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
50495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
50505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
50515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
50535870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
50545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
50555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
50565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
50575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
50585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
50595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
50605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
50615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
50625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
506392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
506492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
506592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
50665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
50675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
50685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
50695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
50705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
50715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
50725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
50735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
50745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
50755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
50775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
50785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
507992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
508092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
508192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
508292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
508392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
508492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
508592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
508692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
508792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
508892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
508992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
5090ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
5091ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
5092ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
5093ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
50945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
50955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
50965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
50975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
50985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
50995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
51015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
51025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
51045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
51055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
51075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
51085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
51095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
51105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
51115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
51125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
51135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
51145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
51155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
51165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
51175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
51185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
51195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
51205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
51215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
51225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
51235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
51245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
51255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
51265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
51275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
51285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5129763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5130763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
513182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5132916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5133916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5134916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
5135916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5136916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5137916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
5138916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
5139916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
5140916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
5141916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
5142916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            String[] projection, String filter) {
5143916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5144916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5145916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5146916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5147916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(" JOIN (SELECT " +
5148916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                RawContacts.CONTACT_ID + " AS snippet_contact_id");
5149916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5150916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA_ID)) {
5151916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", " + DataColumns.CONCRETE_ID + " AS "
5152916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    + SearchSnippetColumns.SNIPPET_DATA_ID);
5153916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5154916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
51559c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA1)) {
51569c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA1 + " AS " + SearchSnippetColumns.SNIPPET_DATA1);
5157916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5158916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
51599c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA2)) {
51609c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA2 + " AS " + SearchSnippetColumns.SNIPPET_DATA2);
5161916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5162916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
51639c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA3)) {
51649c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA3 + " AS " + SearchSnippetColumns.SNIPPET_DATA3);
51659c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
51669c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov
51679c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA4)) {
51689c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA4 + " AS " + SearchSnippetColumns.SNIPPET_DATA4);
5169916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5170916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5171916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_MIMETYPE)) {
5172916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", (" +
5173916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    "SELECT " + MimetypesColumns.MIMETYPE +
5174916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " FROM " + Tables.MIMETYPES +
5175916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " WHERE " + MimetypesColumns._ID + "=" + DataColumns.MIMETYPE_ID +
5176916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    ") AS " + SearchSnippetColumns.SNIPPET_MIMETYPE);
5177916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5178916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5179916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS +
5180916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " WHERE " + DataColumns.CONCRETE_ID +
5181916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " IN (");
5182916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5183916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // Construct a query that gives us exactly one data _id per matching contact.
5184916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // MIN stands in for ANY in this context.
5185916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(
5186916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                "SELECT MIN(" + Tables.NAME_LOOKUP + "." + NameLookupColumns.DATA_ID + ")" +
5187916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
5188916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " JOIN " + Tables.RAW_CONTACTS +
5189916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " ON (" + RawContactsColumns.CONCRETE_ID
5190916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                        + "=" + Tables.NAME_LOOKUP + "." + NameLookupColumns.RAW_CONTACT_ID + ")" +
5191916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME + " GLOB '");
5192916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filter));
5193916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
5194916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + ")" +
5195916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                " GROUP BY " + RawContactsColumns.CONCRETE_CONTACT_ID);
5196916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5197916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(")) ON (" + Contacts._ID + "=snippet_contact_id)");
5198916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5199916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5200916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
5201916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5202916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5203916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void appendContactsTables(StringBuilder sb, Uri uri, String[] projection) {
5204763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5205f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5206763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5207763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5208d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5209763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5210763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
5211b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
521282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_PRESENCE)) {
521382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
521482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ")");
521582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
5216b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
521782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS,
521882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
521982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
522082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
522182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
52223296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
52233296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
5224a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
52253296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
522682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
522782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
5228ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
5229763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
5230763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
5231763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5232f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5233763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5234763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5235d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5236763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5237763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
5238763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
5239763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
5240763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
5241763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
5242763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
524346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private void setTablesAndProjectionMapForRawContactsEntities(SQLiteQueryBuilder qb, Uri uri) {
524446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        // Note: currently, "export only" equals to "restricted", but may not in the future.
524546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
524646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                Data.FOR_EXPORT_ONLY, false);
524746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
5248f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
524946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
525046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        if (requestingPackage != null) {
525146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            excludeRestrictedData = excludeRestrictedData
525246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
525346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        }
525446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setTables(mDbHelper.getContactEntitiesView(excludeRestrictedData));
525546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        qb.setProjectionMap(sRawContactsEntityProjectionMap);
525646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
525746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
525846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
525982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
526082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
526182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5262d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa        // Note: currently, "export only" equals to "restricted", but may not in the future.
5263763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
5264d237c80845d8e13164d34278d3c20e31f8d80b4dDaisuke Miyakawa                Data.FOR_EXPORT_ONLY, false);
5265763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5266f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5267763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5268763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5269763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            excludeRestrictedData = excludeRestrictedData
5270d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton                    || !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5271763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5272763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5273763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getDataView(excludeRestrictedData));
527482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
527582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
52763296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated presence when requested
5277b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.CONTACT_PRESENCE)) {
527882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
52793296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + "="
528082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    + RawContacts.CONTACT_ID + ")");
528182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
528282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
52833296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include aggregated status updates when requested
5284b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
528582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS,
528682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_RES_PACKAGE,
528782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_ICON,
528882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_LABEL,
528982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.CONTACT_STATUS_TIMESTAMP)) {
52903296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
52913296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + ContactsStatusUpdatesColumns.ALIAS +
529282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    " ON (" + ContactsColumns.LAST_STATUS_UPDATE_ID + "="
52933296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
5294ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        }
52953296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
52963296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual presence when requested
52973296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection, Data.PRESENCE)) {
52983296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
52993296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdates.DATA_ID + "="
53003296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    + DataColumns.CONCRETE_ID + ")");
53013296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
53023296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
53033296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        // Include individual status updates when requested
53043296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        if (mDbHelper.isInProjection(projection,
53053296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS,
53063296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_RES_PACKAGE,
53073296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_ICON,
53083296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_LABEL,
53093296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                Data.STATUS_TIMESTAMP)) {
53103296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
53113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
53123296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey                            + DataColumns.CONCRETE_ID + ")");
53133296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey        }
53143296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
531582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
531682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);
531782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
5318ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
5319ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
53200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
53210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
53220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5323b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
53240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
53250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5326b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection, StatusUpdates.PRESENCE)) {
53270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
53280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID
53290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
53300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
53310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5332b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
53330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
53340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
53350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
53360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
53370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
53380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
53390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    " ON(" + Tables.STATUS_UPDATES + "." + StatusUpdatesColumns.DATA_ID
53400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    + "=" + DataColumns.CONCRETE_ID + ")");
53410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
53420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setTables(sb.toString());
53430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
53440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
53450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
53464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
5347f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5348f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5349e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5350e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5351e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5352e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5353fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5354fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5355e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5356e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5357e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5358e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5359e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5360e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
53614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
53624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
53634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
53644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
53654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
53664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
53674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
53684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
53694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5370e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
5371f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5372f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5373e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5374e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5375e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5376e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5377fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5378fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5379e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5380e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5381e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5382e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5383e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5384e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
5385e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
5386e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
5387e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
5388e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
5389e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
5390e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
5391e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
5392e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
5393e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
5394e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
5395e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
5396e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
5397e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
5398e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
5399e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
54007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5401c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
5402c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
5403c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
5404c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
5405c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
5406f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
5407f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String limitParam = getQueryParameter(uri, "limit");
5408c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
5409c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5410c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5411c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
5412c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
5413c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
5414c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
5415c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
5416c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
5417c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5418c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
5419c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
5420c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
5421c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5422c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5423c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5424c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
54255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /**
54265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
54275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
54285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     */
54295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
54305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        int len = cons.length();
54315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
54325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        for (int i = 0; i < len; i++) {
54335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            char c = cons.charAt(i);
54345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
54355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
54365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
54375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
54385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
54395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    || (c == '#') || (c == '*')) {
54405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
54415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
54425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
54435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
54445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
54455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
54465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
54475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
54485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
54495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            return false;
54505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        }
54515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
54525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        return true;
54535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
54545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
545500ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
5456d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
545770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
545870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
5459fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
546070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
546170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
546270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
546370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
5464d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
546570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
546667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
54675ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
54685ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
5469619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
5470619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
5471619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5472b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
5473b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
5474b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
5475b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
5476d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            case CONTACTS_PHOTO: {
5477e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                return openPhotoAssetFile(uri, mode,
5478e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " + RawContacts.CONTACT_ID + "=?",
5479e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5480e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
5481b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5482e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
5483e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                return openPhotoAssetFile(uri, mode,
5484e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
54854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5486d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5487d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5488f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
548942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
549042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                mSelectionArgs1[0] = String.valueOf(lookupContactIdByLookupKey(mDb, lookupKey));
549142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + "=?";
549242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
549342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
549442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
549542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
549642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
549742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                outputRawContactsAsVCard(localStream, selection, mSelectionArgs1);
549842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return buildAssetFileDescriptor(localStream);
549942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
550042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
550142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
550242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
550342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
550442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
550542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
5506d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
5507d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
550842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
550942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
5510d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
551142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
5512d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
551342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
5514d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                    inBuilder.append(lookupContactIdByLookupKey(mDb, lookupKey));
551542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
551642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
551742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
551842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
5519d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5520d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
5521d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
5522d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
5523d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5524d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                outputRawContactsAsVCard(localStream, selection, null);
5525d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return buildAssetFileDescriptor(localStream);
5526d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5527b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5528b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
5529fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
5530fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
5531b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
5532b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
5533b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5534e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,
5535e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
5536e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
5537e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
5538e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
5539e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
5540e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
5541e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5542e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
5543e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
5544e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
5545e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
5546e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql,
5547e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                selectionArgs);
5548e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
5549e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5550d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
5551d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5552d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5553d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
5554d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
5555d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5556d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
5557d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        AssetFileDescriptor fd = null;
5558d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
5559d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
5560d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5561d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
5562d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final int size = byteData.length;
5563d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5564d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
5565d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.writeBytes(byteData, 0, 0, size);
5566d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.deactivate();
5567b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5568d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
5569d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
5570d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
5571d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5572d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        return fd;
5573d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5574d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5575d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5576d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
5577d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
5578d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
5579d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5580d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
5581d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
5582d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
55837a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
55847a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa                new VCardComposer(context, VCardConfig.VCARD_TYPE_DEFAULT, false);
5585d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
5586d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5587f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
55887a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        if (!composer.init(selection, selectionArgs)) {
55897a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa            Log.w(TAG, "Failed to init VCardComposer");
5590d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
55917a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        }
5592d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5593d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
5594d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
5595d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
5596d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5597d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5598d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
5599d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5600b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
56014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
56024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
5603a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
56044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
5605b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
5606be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
56072d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
5608b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
5609b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
5610b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
5611f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
561242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
5613f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
5614b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
5615be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
5616b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
5617b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
5618f481f22a9323fe338672f99b88b26c5f0725cd42David Brown            case DATA:
5619f481f22a9323fe338672f99b88b26c5f0725cd42David Brown                return Data.CONTENT_TYPE;
5620508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
5621b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
562248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
562348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
562448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
562548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
56269005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
56279005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
562848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
562948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
563048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
563148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
563248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
563348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
563448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
563548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
5636b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
5637b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
5638b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
5639b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
5640b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
5641b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
5642b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
5643b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
5644c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
5645c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
5646c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
5647c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
5648d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov
564961efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
565061efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
56514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
56524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
56537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
56545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void setDisplayName(long rawContactId, int displayNameSource,
56555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            String displayNamePrimary, String displayNameAlternative, String phoneticName,
56565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            int phoneticNameStyle, String sortKeyPrimary, String sortKeyAlternative) {
56575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(1, displayNameSource);
56585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 2, displayNamePrimary);
56595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 3, displayNameAlternative);
56605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 4, phoneticName);
56615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(5, phoneticNameStyle);
56625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 6, sortKeyPrimary);
56635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 7, sortKeyAlternative);
56645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(8, rawContactId);
566525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
56663cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
56673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
566873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
566973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
567073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
567173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
5672a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.add(rawContactId);
567373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
567473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
5675c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
5676c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
5677c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
5678c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
5679c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
5680c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
5681653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
5682c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
5683653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
5684653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
5685c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
5686c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
5687c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
5688c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
5689c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
5690c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
5691c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
5692c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
5693c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
5694653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
5695c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
5696653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
5697653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
5698c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
5699c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
5700ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
5701813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov    public String insertNameLookupForEmail(long rawContactId, long dataId, String email) {
5702f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
5703813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return null;
5704f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5705f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5706b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov        String address = mDbHelper.extractHandleFromEmailAddress(email);
5707b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov        if (address == null) {
5708813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return null;
5709f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5710f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5711f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
5712f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
5713813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        return address;
5714f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5715f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5716f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5717f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
5718f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5719f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
5720f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
5721f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
5722f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5723f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5724f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
5725f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
5726f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5727f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5728a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
5729a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
5730a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
5731a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
5732a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
5733a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
5734a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
5735a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
5736a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
5737a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
5738a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
5739f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5740d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name,
5741d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            int fullNameStyle) {
5742d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name, fullNameStyle);
5743f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5744f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5745f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
5746f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5747f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
5748f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
5749f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5750f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5751f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5752f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
5753f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
5754f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
5755f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5756f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5757f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
5758f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
5759d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
5760f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
5761f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5762f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
576348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    public void insertNameLookupForPhoneticName(long rawContactId, long dataId,
576448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            ContentValues values) {
576548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (values.containsKey(StructuredName.PHONETIC_FAMILY_NAME)
576648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                || values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)
576748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                || values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME)) {
576848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId,
576948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
577048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
577148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
577248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
577348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    }
577448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
577548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    public void insertNameLookupForPhoneticName(long rawContactId, long dataId, String familyName,
577648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            String middleName, String givenName) {
577748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        mSb.setLength(0);
577848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (familyName != null) {
577948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(familyName.trim());
578048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
578148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (middleName != null) {
578248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(middleName.trim());
578348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
578448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (givenName != null) {
578548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(givenName.trim());
578648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
578748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
578848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (mSb.length() > 0) {
578948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookup(rawContactId, dataId, NameLookupType.NAME_COLLATION_KEY,
579048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    NameNormalizer.normalize(mSb.toString()));
579148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
57923b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov
57933b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov        if (givenName != null) {
57943b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            // We want the phonetic given name to be used for search, but not for aggregation,
57953b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            // which is why we are using NAME_SHORTHAND rather than NAME_COLLATION_KEY
57963b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            insertNameLookup(rawContactId, dataId, NameLookupType.NAME_SHORTHAND,
57973b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov                    NameNormalizer.normalize(givenName.trim()));
57983b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov        }
579948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    }
580048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
5801f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5802f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
5803f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5804f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
58055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(1, rawContactId);
58065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(2, dataId);
58075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(3, lookupType);
58085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mNameLookupInsert, 4, name);
5809f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
5810f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5811f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
5812f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
5813f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
5814f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
5815f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
58165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupDelete.bindLong(1, dataId);
5817f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
5818f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
5819f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
58202d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
5821d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5822d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
5823d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
5824d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
5825d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
5826d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
5827d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
5828e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
5829916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
5830916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
5831e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
5832e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
58335ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
5834c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
58357318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam);
5836c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
5837c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5838c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
58397318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam) {
58407318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), true);
58415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
58425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
58435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
58447318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov            boolean allowEmailMatch) {
5845d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
5846dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov                "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
5847d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
5848d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
5849d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " GLOB '");
58505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
5851a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
5852a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NAME_COLLATION_KEY + ","
5853a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NICKNAME + ","
58544cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                + NameLookupType.NAME_SHORTHAND + ","
5855f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                + NameLookupType.ORGANIZATION + ","
585689f1f71495aedc58252b3f58a46a036986c319d2Dmitri Plotnikov                + NameLookupType.NAME_CONSONANTS);
585720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        if (allowEmailMatch) {
585820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
585920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        }
586089f1f71495aedc58252b3f58a46a036986c319d2Dmitri Plotnikov        sb.append("))");
5861ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
5862ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
58634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
58644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
58654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
58664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
5867b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
5868b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
5869b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
5870b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
5871b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
58724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
58734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
5874b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
5875b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
5876b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
5877caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
58785e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
58795e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
58805e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
58815e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
58825e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
58835e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
58845e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
58855e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
58865e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
58875e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
58885e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
5889caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
5890caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
5891caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
5892df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
5893df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
5894caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
5895caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
5896caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
5897caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
58986f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
5899caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
59006f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
5901caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
5902f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
590373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
590473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
590573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
590673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
5907bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
5908bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
5909bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
5910bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
591173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
591273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
591373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
591473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
591573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
5916627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
5917627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
5918627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
5919627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
592073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
592173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
592273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
5923627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
5924627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
5925627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
5926627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
5927627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
592873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
592973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
593073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
593173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
593273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
593373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
593473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
5935627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
5936b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
5937f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
5938f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
5939f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5940f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
5941f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5942f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5943f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5944f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5945f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5946f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
5947f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
5948f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
5949f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5950f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5951f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
5952f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5953f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
5954f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
5955f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5956f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5957f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
5958f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
5959f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
5960f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
5961f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
5962f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
5963f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5964f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
5965f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
5966f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
5967f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
5968f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
5969f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
5970f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
5971f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5972f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5973f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
5974f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
5975f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5976f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
5977f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
5978f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
5979f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
5980f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
5981f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5982f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5983f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5984f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
5985f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5986f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
5987f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
5988f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5989f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5990f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
5991f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
5992f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
5993f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
5994f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
5995f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
5996f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
5997f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
5998f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
5999f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
6000f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
6001f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6002f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6003f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
6004f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
60055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
60065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void bindString(SQLiteStatement stmt, int index, String value) {
60075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (value == null) {
60085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindNull(index);
60095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
60105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindString(index, value);
60115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
60125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
60135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
60145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void bindLong(SQLiteStatement stmt, int index, Number value) {
60155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (value == null) {
60165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindNull(index);
60175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
60185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindLong(index, value.longValue());
60195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
60205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
60212a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
60222a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov    protected boolean isAggregationUpgradeNeeded() {
60232a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
60242a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            return false;
60252a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        }
60262a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
60272a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_AGGREGATION_ALGORITHM, "1"));
60282a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        return version < PROPERTY_AGGREGATION_ALGORITHM_VERSION;
60292a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov    }
60302a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
60312a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov    protected void upgradeAggregationAlgorithm() {
60322a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        // This upgrade will affect very few contacts, so it can be performed on the
60332a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        // main thread during the initial boot after an OTA
60342a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov
60352a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        Log.i(TAG, "Upgrading aggregation algorithm");
60362a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        int count = 0;
60372a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
60382a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        try {
60392a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            mDb.beginTransaction();
60402a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            Cursor cursor = mDb.query(true,
60412a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    Tables.RAW_CONTACTS + " r1 JOIN " + Tables.RAW_CONTACTS + " r2",
60422a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    new String[]{"r1." + RawContacts._ID},
60432a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    "r1." + RawContacts._ID + "!=r2." + RawContacts._ID +
60442a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    " AND r1." + RawContacts.CONTACT_ID + "=r2." + RawContacts.CONTACT_ID +
60452a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_NAME + "=r2." + RawContacts.ACCOUNT_NAME +
60462a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    " AND r1." + RawContacts.ACCOUNT_TYPE + "=r2." + RawContacts.ACCOUNT_TYPE,
60472a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    null, null, null, null, null);
60482a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            try {
60492a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                while (cursor.moveToNext()) {
60502a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    long rawContactId = cursor.getLong(0);
60512a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId,
60522a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                            RawContacts.AGGREGATION_MODE_DEFAULT, true);
60532a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    count++;
60542a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                }
60552a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            } finally {
60562a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                cursor.close();
60572a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            }
60582a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            mContactAggregator.aggregateInTransaction(mDb);
60592a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            mDb.setTransactionSuccessful();
60602a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            mDbHelper.setProperty(PROPERTY_AGGREGATION_ALGORITHM,
60612a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    String.valueOf(PROPERTY_AGGREGATION_ALGORITHM_VERSION));
60622a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        } finally {
60632a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            mDb.endTransaction();
60642a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            long end = SystemClock.currentThreadTimeMillis();
60652a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov            Log.i(TAG, "Aggregation algorithm upgraded for " + count
60662a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov                    + " contacts, in " + (end - start) + "ms");
60672a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov        }
60682a0d5f9c628e723261bc5198e0fd606076b76b74Dmitri Plotnikov    }
60694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
6070