ContactsProvider2.java revision 09ae48b82b17e24016b14a1ab64706222ab1071f
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
1997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.internal.content.SyncStateContentProviderHelper;
2097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
2197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
2297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
2497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
2597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
2697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
2797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
2897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
2997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
3697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
3797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
3897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardComposer;
3997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardConfig;
4097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
4197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
4297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
4397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
44b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
45caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
465b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
47bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.Notification;
48bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.NotificationManager;
49bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.PendingIntent;
50c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
51568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
52568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
536ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
5435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
5567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
5667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
57627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
58bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.Intent;
59568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
603d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
61627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
6267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
63b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.content.res.AssetFileDescriptor;
644cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Taoimport android.content.res.Configuration;
651129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikovimport android.database.CharArrayBuffer;
664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
67ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
68ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
6909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
7009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
71a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
72b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport android.database.sqlite.SQLiteContentHelper;
734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
75c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
77d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
7851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikovimport android.os.AsyncTask;
796ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
80d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport android.os.MemoryFile;
81b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
820e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
833d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
84508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
853de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
86b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
8797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
8897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
8997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
9097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
9197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
9297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
9397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
9497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
9597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
9697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
97ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
983de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
993de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
100d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
1015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.DisplayNameSources;
1025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.FullNameStyle;
1033de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
104bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1053de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
1065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.PhoneticNameStyle;
10709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1083de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
109916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1103de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
11182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
11297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
11397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
11497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
115a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
116a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
117c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
119d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
120b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
121d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
122d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
12342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
12642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
127b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1280e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
130622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
131b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1320e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
133ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1395b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
140caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
141bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
142bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
143bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
145619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
146619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
147619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
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
1600e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1610e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
162a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
164dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov    private static final String TIMES_CONTACTED_SORT_COLUMN = "times_contacted_sort";
1655e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
166d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
167dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov            + TIMES_CONTACTED_SORT_COLUMN + " DESC, "
1689b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
169d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
170d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
171d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
172d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1736e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
1749b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
1759b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1769b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
1779b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
1786e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
1799b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
1809b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1819b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
1829b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
183de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
184de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
185d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
186d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
189a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
1905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
194a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
195f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
19642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private static final int CONTACTS_AS_MULTI_VCARD = 1011;
1972149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_DATA = 1012;
1982149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_DATA = 1013;
199a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_ENTITIES = 1014;
200a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ENTITIES = 1015;
201a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1016;
2024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
20646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
2074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
210ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
21148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
21248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
21348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
21448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
21548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
21648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
21748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
21848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
219a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
222b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
223b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
224b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
22582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
22682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
22831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
22931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
230eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
231eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
232ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
233ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
234ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
235ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
23635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
237b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
23835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
239c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
240c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
241c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
24746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
24846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
24909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
25009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
251d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
252d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
253d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
254dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
255dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
256dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
257dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
258dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
259dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE
260dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
261dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
262dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
263dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
264dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
265dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
266dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
267dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND "
268dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + Groups.AUTO_ADD + " != 0";
269dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
270dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
271dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
272dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
273dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
274dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
275dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
276dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
277dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
278dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
279dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
280dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
281d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
282f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
283f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
284f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
28567dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
28667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2876cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
289f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
290ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
291ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
292d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
29367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
294d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
295ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
29714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
29867dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
2993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
30088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
3013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
3023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
3035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
3043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
305f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
30688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
30788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
30888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
30988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
31088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
31188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
31288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
313f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
3143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
3153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
31614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
3173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
3185ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
3193cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
320f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
3213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
3223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
32314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
324321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
32520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
32620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
327321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
328321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
32920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
33020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
331f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
33219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private interface RawContactsQuery {
33319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
33419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
33519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
336ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
337ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
338ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
33919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
34019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
34119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
342ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
343ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
34419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
34519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
346c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
347df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
348caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
34971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
35071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
35171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
35271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
35371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
35471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
35571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
35671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
35771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
35871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
35971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
36071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
36171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
36271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
363a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
364a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
365a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
366a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
367a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
368a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
369a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
370a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
371a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
372a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
373a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
374a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
375916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
376916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
377916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
378916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
379916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NICKNAME + "," +
380916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_SHORTHAND + "," +
381f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.ORGANIZATION + "," +
382f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.NAME_CONSONANTS;
383916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
384916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
385f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
386f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
387f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
388f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
389f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
390f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
391f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
392f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
393f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
394f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
395f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
396f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
397f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
398f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
399f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
400f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
401f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
402f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
403f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
404f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
405f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
406f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
407f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
408f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
409f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
410f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
411f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
412f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
413f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
414f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
415f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
416f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
417f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
418f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
419f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
420f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
421f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_MIMETYPE)
422f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA_ID)
423f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA1)
424f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA2)
425f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA3)
426f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA4)
427f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
428f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
429f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
430f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
431f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
432f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
433f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
434f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
435f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
436f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
437f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
438f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
439f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
440f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
441f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
442f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
443f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
444f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
445f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
446f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
447f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
448f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
449f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
450f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
451f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
452f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
453f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
454f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
455f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
456f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
457f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
458f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
459f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
460f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
461f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
462f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
463f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
464f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
465f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
466f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
467f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
468f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
469f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
470f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
471f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
472f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
473f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
474f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
475f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
476f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
477f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
478f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
479f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
480f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
481f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
482f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
483f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
484f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
485f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
486f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
487f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
488f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
489f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
490f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
491f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
492f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
493f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
494f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
495f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
496f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
497f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
498f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
499f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
500f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
501038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
502f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
503f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
504f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
505f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
506e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
507f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
508f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
509f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
510f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
511f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
512f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
513f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
514f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
515916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
516f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
517f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
518f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
519f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
520916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5215e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
522f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
523f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
524f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
525f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, Contacts.TIMES_CONTACTED)
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
532f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
533f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
538ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
539f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
560a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
561f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.IS_RESTRICTED)
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
573a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
574f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.IS_RESTRICTED)
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
5904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
591f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
604f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6139261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
614f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
630f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
631ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
633f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
655ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
657f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ")")
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Contacts.HAS_PHONE_NUMBER + ")")
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
672373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
674f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
680eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
682f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
698f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
701f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
704f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
705f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
709f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
710f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
712f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
716f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
717f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
71882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
720f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
742f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
749d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
751f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
7607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
7619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
7629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
7639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
7649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
7659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
7669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
7672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
7682526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
769bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
770bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
771bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
772bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
77351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
774c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
775c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
7763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
777c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
7783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
77925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
78082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Precompiled sql statement for updating an aggregated status update */
781a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mLastStatusUpdate;
782f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
783f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupDelete;
784a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateAutoTimestamp;
785a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateInsert;
786a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateReplace;
7870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private SQLiteStatement mStatusAttributionUpdate;
788a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateDelete;
789f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov    private SQLiteStatement mResetNameVerifiedForOtherRawContacts;
790a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
791f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdEmail;
792f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdIm;
7931129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdStructuredName;
7941129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdOrganization;
7951129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdNickname;
7961129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdPhone;
797f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
7981129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
7991129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
8002526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
8012526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
802f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
803f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
8044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
8054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
806a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
807d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
808d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
809a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
810a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
8113653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
8123653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
8132d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
8142d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
815a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
816c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
8175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
8185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
8192149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
8205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
8212149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
8222149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
823a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
824a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
825a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
826a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
827f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
82842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
82942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
8305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
831ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
832ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
8335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
8343653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
8355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
8365ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
8375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
83846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
83946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
84046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
841b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
8424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
8434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
844ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
84548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
8465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
847ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
8484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
84948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
8505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
8515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
8524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
853ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
85448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
8551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
856ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
857ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
858ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
859ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
86035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
861b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
862b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
86335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
864a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
865b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
866b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
867b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
868b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
8694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
870eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
871eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
87282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
87382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
8741f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
875c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
876c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
877c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
878c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
8792d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
880c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
881c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
8821b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
8831b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
8841b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
8851b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
8861b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
8871b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
8881b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
8891b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
89009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
89109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
892d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
893d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
894d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
89519a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
89619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
897d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
898d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
899d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
900d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
901d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
902d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
903d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
904d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
905d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
906d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache;
907d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
9083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
9093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
9103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
9113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
9123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
914653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
9153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9161129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        @SuppressWarnings("all")
9173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
9183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
919a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
920a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
921a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
922a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
923a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
924a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
925a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
926a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
9273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
929653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
930653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
931b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
932653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
933653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
934653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
935653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
9363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
9373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
9383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
9395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
940e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
941e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
942e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
943e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
944653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
945e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
946e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
947e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
9483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
9513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
9523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
953813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         *
954813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * @return true if update changed something
9553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
956813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
957f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
95814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
95914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
960653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
961653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
962653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
963653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
964653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
965653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
966653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
967653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
968653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
969653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
970653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
971653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
972653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
973653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
974653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
975653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
976653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
9774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
9784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " =?", mSelectionArgs1);
979653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
980653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
981f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
982653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
983653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
984813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
985813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
9863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
98914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
99014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
99114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
9924da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
9934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=?", mSelectionArgs1);
9944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
9954da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
9963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
9975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
9983cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
9993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
10003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
10034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            long mimeTypeId = getMimeTypeId();
1004e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
1005e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
10064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
10074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            Cursor c = db.query(DataDeleteQuery.TABLE,
10084da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    DataDeleteQuery.CONCRETE_COLUMNS,
10094da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=?" +
10104da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        " AND " + DataColumns.MIMETYPE_ID + "=" + mimeTypeId,
10114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
10123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
1013e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
101414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
1015f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
1016e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
1017e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
1018e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
1019e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
10203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
10213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
10223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
10233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            if (primaryId != -1) {
10254da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                setIsPrimary(rawContactId, primaryId, mimeTypeId);
10264da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            }
1027e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1028e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1029e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
1030e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
1031e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
1032e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
1033e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1034e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
10353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
103725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
1038285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1039d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                updateRawContactDisplayName(db, rawContactId);
1040fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(db, rawContactId);
1041285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
10423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1043a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1044622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1045622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
1046813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * as baseline, but augmented with any updates.  Returns null if there is
1047813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * no change.
1048622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1049622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
1050622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
1051813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            boolean changing = false;
1052622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
10534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
10544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=?",
10554da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
1056622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
1057622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
1058622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
1059622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
1060813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        final String value = cursor.getString(i);
1061813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        if (!changing && update.containsKey(key)) {
1062813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            Object newValue = update.get(key);
1063813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            String newString = newValue == null ? null : newValue.toString();
1064813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            changing |= !TextUtils.equals(newString, value);
1065813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        }
1066813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        values.put(key, value);
1067622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
1068622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
1069622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
1070622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1071622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1072813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!changing) {
1073813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return null;
1074813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1075813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1076622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1077622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1078622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
10793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
10823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
10843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
10853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
1089622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
10903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1091622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
10923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1093622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
10943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10963cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
10975ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1098622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
109914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
110014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
110114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1102f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1103d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            Integer fullNameStyle = values.getAsInteger(StructuredName.FULL_NAME_STYLE);
1104d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name,
110551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                    fullNameStyle != null
110651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            ? mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)
110751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            : FullNameStyle.UNDEFINED);
110848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId, values);
110925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1110813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
111114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
111214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
111314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
111414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1115813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1116f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1117622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1118622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1119cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1120622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1121813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {  // No change
1122813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1123813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1124813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1125622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
112614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1127f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
11287ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME) ||
11297ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_FAMILY_NAME) ||
11307ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME) ||
11317ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)) {
11327ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                augmented.putAll(values);
11337ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                String name = augmented.getAsString(StructuredName.DISPLAY_NAME);
1134f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
11357ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                Integer fullNameStyle = augmented.getAsInteger(StructuredName.FULL_NAME_STYLE);
1136d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name,
113751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                        fullNameStyle != null
113851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                ? mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)
113951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                : FullNameStyle.UNDEFINED);
11407ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                insertNameLookupForPhoneticName(rawContactId, dataId, augmented);
114114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
114225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1143813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
1144813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
114514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
114614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
114714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
114814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
114914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
115014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
115114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
115214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
115314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1154f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
115525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1156813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
115714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
11583cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11593cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1161622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
11623cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1163622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1164622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1165622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1166622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
11673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1168622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1169622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1170622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1171622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1172622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1173622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1174622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
117567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1176622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
117767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
117867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1179622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1180622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
11818c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1182622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1183622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
118467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
118567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
118667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
118767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
118867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
118967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
11908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1191622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
11924cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                // As the name could be changed, let's guess the name style again.
11934cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                name.fullNameStyle = FullNameStyle.UNDEFINED;
11944cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                mSplitter.guessNameStyle(name);
1195ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                int unadjustedFullNameStyle = name.fullNameStyle;
1196ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                name.fullNameStyle = mSplitter.getAdjustedFullNameStyle(name.fullNameStyle);
11975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                final String joined = mSplitter.join(name, true);
1198622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
11995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
1200ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                update.put(StructuredName.FULL_NAME_STYLE, unadjustedFullNameStyle);
12015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
12024cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao            } else if (touchedUnstruct && touchedStruct){
1203d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.FULL_NAME_STYLE)) {
1204d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.FULL_NAME_STYLE,
1205d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessFullNameStyle(unstruct));
12064cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1207d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.PHONETIC_NAME_STYLE)) {
1208d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.PHONETIC_NAME_STYLE,
1209d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessPhoneticNameStyle(unstruct));
12104cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1211622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1212622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1213622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1214622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1215622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1216622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1217622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1218622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1219622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1220622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1221622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1222622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1223622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1224622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1225622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1226622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1227622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1228622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1229622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1230813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1231f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1232622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1233622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1234813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {    // No change
1235813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1236813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1237813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1238622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1239f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1240813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1241622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1242622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1243622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1244622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1245622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1246622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1247622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1248622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1249622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1250622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1251622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1252622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1253622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1254622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1255622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1256622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1257622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1258622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
125967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
126067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
126167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
126267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1263622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1264622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1265622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1266622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1267622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1268622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
126967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
127067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
127167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1272622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1273622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1274622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
12753cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
12763cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12773cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
12803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
12823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
12833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
12853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
12863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
12873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
12883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12915ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1292622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1293622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1294622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
12953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1296622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1297813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1298f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1299622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1300622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1301813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {        // No change
1302813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1303813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1304622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1305813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return super.update(db, values, c, callerIsSyncAdapter);
1306622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
13073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1308622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1309622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1310622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1311622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1312622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1313622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1314622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1315622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
13163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1317622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1318622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1319622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1320622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1321622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
13223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
13263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
13283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
13293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1333a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1334a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1335a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1336a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1337a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
133825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1339a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1340a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
13413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
1344813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1345f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1346813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1347813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1348813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
134914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
135031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsCompany = values.containsKey(Organization.COMPANY);
135131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsTitle = values.containsKey(Organization.TITLE);
135231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            if (containsCompany || containsTitle) {
1353813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1354813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1355813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
135631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String company;
135731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
135831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsCompany) {
135931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = values.getAsString(Organization.COMPANY);
136031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
136131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
136231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = DatabaseUtils.stringForQuery(db,
136331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.COMPANY +
136431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
136531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
136631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
136731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
136831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String title;
136931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsTitle) {
137031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = values.getAsString(Organization.TITLE);
137131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
137231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
137331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = DatabaseUtils.stringForQuery(db,
137431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.TITLE +
137531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
137631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
137731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
137831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
137931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                deleteNameLookup(dataId);
138031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                insertNameLookupForOrganization(rawContactId, dataId, company, title);
138131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
138231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
138331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            }
1384813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
138514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
138614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
138714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
138814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1389a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
139014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
139114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
139214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
139325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1394a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
139514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
139614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
139714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
139814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
13993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
14003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
14013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
14023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
14033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
14043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
14053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
14063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
14083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1409e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1410e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1411e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1412e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1413e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1414e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1415e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
14165ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1417813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String email = values.getAsString(Email.DATA);
141814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
141914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
142014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
142125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1422813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String address = insertNameLookupForEmail(rawContactId, dataId, email);
1423813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (address != null) {
1424813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1425813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
142614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
142714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
142814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
142914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1430813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1431f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1432813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1433813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1434813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
143514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1436b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Email.DATA)) {
1437813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1438813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1439813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1440b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String address = values.getAsString(Email.DATA);
1441b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                deleteNameLookup(dataId);
1442b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                insertNameLookupForEmail(rawContactId, dataId, address);
1443b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1444813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1445b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1446813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1447813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
144814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
144914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
145014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
145114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
145214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
145314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
145414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
145514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
145614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1457f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
145825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1459813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
146014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1461e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1462e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1463e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1464e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1465e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1466e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1467e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1468e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1469e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1470e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1471e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1472e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1473e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1474e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
147514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
147614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
147714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
147814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
147914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
148014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
148114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
148214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
148314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
148414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
148514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
148614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1487813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!TextUtils.isEmpty(nickname)) {
1488813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1489813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                insertNameLookupForNickname(rawContactId, dataId, nickname);
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) {
149814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
149914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
150014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1501813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1502813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1503813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
150414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1505b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Nickname.NAME)) {
1506b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String nickname = values.getAsString(Nickname.NAME);
1507b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                deleteNameLookup(dataId);
1508b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                insertNameLookupForNickname(rawContactId, dataId, nickname);
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;
152714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
152814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
152914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
15303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
15313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
15333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
15343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
15353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
15375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
15380b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
15390b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
15400b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
1541813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                String normalizedNumber = computeNormalizedNumber(number);
1542813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
15430b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1544653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
15450b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1546285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
154725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1548813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                if (normalizedNumber != null) {
1549813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                    triggerAggregation(rawContactId);
1550813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                }
15510b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
15520b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
15530b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1554653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1555653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1556653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1557653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1558813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1559f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1560813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String number = null;
1561813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String normalizedNumber = null;
15620b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1563813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                number = values.getAsString(Phone.NUMBER);
1564813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                normalizedNumber = computeNormalizedNumber(number);
1565813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                values.put(PhoneColumns.NORMALIZED_NUMBER, normalizedNumber);
1566813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1567653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1568813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1569813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1570813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1571653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1572813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1573813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1574813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
15750b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, normalizedNumber);
1576285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
157725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1578813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
15790b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1580813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
158114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
158214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
158314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
158414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
158514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
158614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
158714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
158814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
158914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
159014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1591285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
159225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1593813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
159414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1595653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1596653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1597813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        private String computeNormalizedNumber(String number) {
1598e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            String normalizedNumber = null;
1599e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1600e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                normalizedNumber = PhoneNumberUtils.getStrippedReversed(number);
1601e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1602653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return normalizedNumber;
1603653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1604e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1605653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1606653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                String number, String normalizedNumber) {
1607e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1608653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues phoneValues = new ContentValues();
16095ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1610653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1611e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
161236045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.MIN_MATCH,
161336045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov                        PhoneNumberUtils.toCallerIDMinMatch(number));
161436045476d2cc7c9c2f985307e87cb6bbc4cfe434Dmitri Plotnikov
1615653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                db.replace(Tables.PHONE_LOOKUP, null, phoneValues);
1616653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else {
16174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
16184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=?", mSelectionArgs1);
1619e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
16203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
16213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
16223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
16233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
16243cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
16253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
16263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
16273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
16283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
16293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
16303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
16313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
16323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
16333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
16343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
16353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
16363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
16373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1638653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1639653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1640dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private static final String SELECTION_RAW_CONTACT_ID = RawContacts._ID + "=?";
1641dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1642dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private static final String QUERY_COUNT_FAVORITES_GROUP_MEMBERSHIPS_BY_RAW_CONTACT_ID =
1643dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                "SELECT COUNT(*) FROM " + Tables.DATA + " LEFT OUTER JOIN " + Tables .GROUPS
1644dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " ON " + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID
1645dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + "=" + GroupsColumns.CONCRETE_ID
1646dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
1647dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " AND " + Tables.DATA + "." + GroupMembership.RAW_CONTACT_ID + "=?"
1648dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " AND " + Groups.FAVORITES + "!=0";
1649dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1650653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1651653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1652653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1653653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1654653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1655653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1656653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
16570be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1658dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (hasFavoritesGroupMembership(db, rawContactId)) {
1659dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, true /* starred */);
1660dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
16610be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
16620be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1663653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1664653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1665653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1666813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1667f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
166814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1669dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
1670653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1671813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1672813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1673813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1674dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
1675dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (wasStarred != isStarred) {
1676dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, isStarred);
1677dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
16780be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
1679813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
16800be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
16810be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
1682dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private void updateRawContactsStar(SQLiteDatabase db, long rawContactId, boolean starred) {
1683dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            ContentValues rawContactValues = new ContentValues();
1684dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            rawContactValues.put(RawContacts.STARRED, starred ? 1 : 0);
1685dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (db.update(Tables.RAW_CONTACTS, rawContactValues, SELECTION_RAW_CONTACT_ID,
1686dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{Long.toString(rawContactId)}) > 0) {
1687dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mContactAggregator.updateStarred(rawContactId);
1688dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1689dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1690dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1691dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private boolean hasFavoritesGroupMembership(SQLiteDatabase db, long rawContactId) {
1692dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final long groupMembershipMimetypeId = mDbHelper
1693dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
1694dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = 0 < DatabaseUtils
1695dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    .longForQuery(db, QUERY_COUNT_FAVORITES_GROUP_MEMBERSHIPS_BY_RAW_CONTACT_ID,
1696dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{Long.toString(groupMembershipMimetypeId), Long.toString(rawContactId)});
1697dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return isStarred;
1698dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1699dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
17000be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
17010be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
17020be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1703dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
17040be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
1705dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
1706dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (wasStarred && !isStarred) {
1707dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, false /* starred */);
1708dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
17090be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
17100be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
17110be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
17120be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
17130be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
1714b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
17150be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
1716b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
17170be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1718653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1719653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1720653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1721653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1722653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1723653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1724653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1725653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1726653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1727653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1728653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1729653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1730653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1731653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1732653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1733653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1734653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1735653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1736653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1737653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1738653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1739653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1740653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1741653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1742ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
1743ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        mInsertedRawContacts.get(rawContactId));
1744653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1745653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1746653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1747653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1748653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1749653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1750a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1751a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1752a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1753a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1754a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1755a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1756a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1757a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1758a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1759285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1760285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1761285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1762a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1763a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1764a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1765a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1766813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1767f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1768a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1769813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1770813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1771813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1772813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1773a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1774813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1775a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1776a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1777a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1778a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1779a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1780a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1781a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1782a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1783a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1784a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1785a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1786ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    /**
1787ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1788ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1789ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1790ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    public class GroupIdCacheEntry {
1791ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1792ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1793ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1794ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1795ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1796a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
17973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1798b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
179931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
18004097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1801f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1802315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1803622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1804622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1805ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1806ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1807ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // is a list of groups with this group id.
1808ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1809ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
181072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
1811622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1812f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1813a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1814d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1815a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
181620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
18171129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private CharArrayBuffer mCharArrayBuffer = new CharArrayBuffer(128);
18185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private NameSplitter.Name mName = new NameSplitter.Name();
181973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
182020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
182109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
182209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
1823ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
182473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1825ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<Long, Account> mInsertedRawContacts = Maps.newHashMap();
1826b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1827a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private HashSet<Long> mDirtyRawContacts = Sets.newHashSet();
1828b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1829de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
18301a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
18311a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
183281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
183381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
18344cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
1835d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
183673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
183772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
18384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
18394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1840de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1841ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1842ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1843ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1844ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1845ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1846ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1847ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
184835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1849ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
1850de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
1851b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
185272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1853a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1854b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1855767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
1856767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov                createPhotoPriorityResolver(context));
18570e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
1858a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
1859b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
1860653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
186151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
1862d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1863b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mSetPrimaryStatement = mDb.compileStatement(
1864653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1865653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1866653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1867653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1868653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1869b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mSetSuperPrimaryStatement = mDb.compileStatement(
1870653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1871653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1872653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1873653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1874653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1875653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1876653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1877653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1878653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1879653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1880653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1881b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mRawContactDisplayNameUpdate = mDb.compileStatement(
188225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
18835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                " SET " +
18845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_SOURCE + "=?," +
18855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_PRIMARY + "=?," +
18865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_ALTERNATIVE + "=?," +
18875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.PHONETIC_NAME + "=?," +
18885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.PHONETIC_NAME_STYLE + "=?," +
18895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.SORT_KEY_PRIMARY + "=?," +
18905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.SORT_KEY_ALTERNATIVE + "=?" +
189125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
18923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1893b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mLastStatusUpdate = mDb.compileStatement(
1894a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                "UPDATE " + Tables.CONTACTS +
1895a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1896a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "(SELECT " + DataColumns.CONCRETE_ID +
1897a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " FROM " + Tables.STATUS_UPDATES +
1898a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.DATA +
1899a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1900a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1901a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
1902a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1903a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1904a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
19050a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
19060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                                + StatusUpdates.STATUS +
1907a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                        " LIMIT 1)" +
1908a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1909e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1910b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mNameLookupInsert = mDb.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1911f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1912f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1913f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1914b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mNameLookupDelete = mDb.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1915f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1916f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1917b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateInsert = mDb.compileStatement(
1918a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1919a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
19200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
19210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
19220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
19230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
19240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?)");
1925a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1926b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateReplace = mDb.compileStatement(
1927a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1928a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
19290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
19300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
19310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
19320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
19330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
19340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1935a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1936b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateAutoTimestamp = mDb.compileStatement(
1937a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
19380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
19390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
1940a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
19410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
19420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
1943b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusAttributionUpdate = mDb.compileStatement(
19440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
19450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
19460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
19470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
19480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1949a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1950b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateDelete = mDb.compileStatement(
1951a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1952a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1953a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1954f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        // When setting NAME_VERIFIED to 1 on a raw contact, reset it to 0
1955f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        // on all other raw contacts in the same aggregate
1956b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mResetNameVerifiedForOtherRawContacts = mDb.compileStatement(
1957f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
1958f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " SET " + RawContacts.NAME_VERIFIED + "=0" +
1959f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " WHERE " + RawContacts.CONTACT_ID + "=(" +
1960f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        "SELECT " + RawContacts.CONTACT_ID +
1961f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1962f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        " WHERE " + RawContacts._ID + "=?)" +
1963f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " AND " + RawContacts._ID + "!=?");
1964f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
1965f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1966f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
19671129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdStructuredName = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE);
19681129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdOrganization = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE);
19691129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdNickname = mDbHelper.getMimeTypeId(Nickname.CONTENT_ITEM_TYPE);
19701129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdPhone = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
197104b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov
1972bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        verifyAccounts();
1973bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
197465ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
197565ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov            importLegacyContactsAsync();
197680952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov        } else {
197780952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov            verifyLocale();
197865ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        }
197965ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
198072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        startContactDirectoryManager();
198172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1982b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return (mDb != null);
19834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
19844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1985ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao    private void initDataRowHandlers() {
1986ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers = new HashMap<String, DataRowHandler>();
1987ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao
1988ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
1989ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
1990ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
1991ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
1992ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
1993ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
1994ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
1995ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
1996ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
1997ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new StructuredNameRowHandler(mNameSplitter));
1998ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
1999ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new StructuredPostalRowHandler(mPostalSplitter));
2000ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
2001ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
2002ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao    }
200372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
200451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
2005767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov     * Visible for testing.
2006767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov     */
2007767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
2008767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov        return new PhotoPriorityResolver(context);
2009767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    }
2010767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov
2011767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
201251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
201351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
201404b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
20154cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
201604b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
20174cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
20184cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
201951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
2020cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
2021ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao        initDataRowHandlers();
20224cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
20234cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
20244cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    @Override
202551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    public void onConfigurationChanged(Configuration newConfig) {
20264f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
20274f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
20284f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
20294f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
203051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
203151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        verifyLocale();
20324cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
203351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2034c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
2035c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
2036c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
2037c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
2038c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
203951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
204051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
204151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
204251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
204351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
204451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
204551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
204651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void verifyLocale() {
2047f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2048f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
2049f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
2050f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
2051f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
2052f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
205351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
205451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
205551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
205651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
205751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
205851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
205951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
206051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
206151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
206251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
206351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        AsyncTask<Integer, Void, Void> task = new AsyncTask<Integer, Void, Void>() {
206451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
206551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            int savedProviderStatus;
206651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
206751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
206851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected Void doInBackground(Integer... params) {
206951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                savedProviderStatus = params[0];
207051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, currentLocale);
207151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                return null;
207251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
207351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
207451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
207551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected void onPostExecute(Void result) {
207651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).commit();
207751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                setProviderStatus(savedProviderStatus);
2078f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2079f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // Recursive invocation, needed to cover the case where locale
2080f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // changes once and then changes again before the db upgrade is completed.
2081f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                verifyLocale();
208251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
208351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        };
208451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
208551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        task.execute(providerStatus);
208651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
208751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
208831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
2089de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
2090b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
2091b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
209231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
209331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
2094013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
2095013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
2096013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
2097013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
20985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
209972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManager() {
210072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
210172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
210272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
210372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
21045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
21055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
21065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
21075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
210872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
210972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    protected void startContactDirectoryManager() {
211072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        getContactDirectoryManager().start();
211172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
211272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
21133d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
2114b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
2115b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
21163d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
21173d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2118568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
2119568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
2120568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2121568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2122568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2123568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
2124568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
2125568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2126568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
2127bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
2128bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
2129bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch == null) {
2130bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            mAccessLatch = new CountDownLatch(1);
2131bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2132568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2133568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
2134568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
2135568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
213680952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                final SharedPreferences prefs =
213780952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                    PreferenceManager.getDefaultSharedPreferences(getContext());
213880952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, mCurrentLocale);
213980952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
214080952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov
2141bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                LegacyContactImporter importer = getLegacyContactImporter();
2142bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (importLegacyContacts(importer)) {
2143bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportSuccess();
2144bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2145bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportFailure();
2146568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
2147568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
2148568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
2149568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2150568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
2151568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2152568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2153bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2154bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
2155bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2156bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
2157bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2158bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
2159bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
2160bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2161b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
2162b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
2163b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
2164bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
2165bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch.countDown();
2166bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch = null;
2167bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
2168bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
2169bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2170bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2171bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
2172bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2173bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
2174bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
2175bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2176bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
2177bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2178bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
2179bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
2180bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
2181bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
2182bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
2183bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
2184bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
2185bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
2186bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
2187bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2188bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
2189bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2190bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
2191bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
21923d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
21933d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
21943d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
2195568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
21960e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
21973d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
21983d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
2199bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
2200bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2201bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
2202bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
2203bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
2204bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
22053d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
22063d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
22073d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
2208bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
2209bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
22103d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
22113d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2212a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
2213a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
2214a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
2215a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
2216b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
2217a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
2218a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2219568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2220568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
2221568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
2222568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
2223568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
2224568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
2225568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2226568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
2227ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
2228ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
2229ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
2230ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
2231ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
2232ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
2233ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
2234ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
223581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
2236ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
2237ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
2238568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
2239568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2240568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2241568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2242568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
2243568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2244568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
2245568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2246568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2247568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2248568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
2249bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch != null) {
2250bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2251bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2252bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2253bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2254bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (match == PROVIDER_STATUS && isLegacyContactImportNeeded()) {
2255bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2256bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2257bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    importLegacyContactsAsync();
2258bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2259bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2260bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2261bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2262bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2263bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2264568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2265568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
2266568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2267568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2268568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2269568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
2270568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2271568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
2272568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2273568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2274568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2275568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2276568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
2277568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2278568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
2279568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2280568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
22814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2282285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
2283bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2284b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2285b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2286285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
22871ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
2288b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2289b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2290b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2291b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
2292285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
2293b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
2294df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
2295a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.clear();
2296285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2297285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2298285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2299285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
23001129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
2301bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2302b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2303b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2304285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
2305b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
23061ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
23071a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
23081a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
2309b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
23101a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
2311b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2312b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2313b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2314bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2315b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2316b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
23171129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
231808e42c9c153a60bf2e7c71dd40bf84bb5fc93555Dmitri Plotnikov        for (long rawContactId : mInsertedRawContacts.keySet()) {
2319d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            updateRawContactDisplayName(mDb, rawContactId);
2320d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
2321285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
2322b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2323a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        if (!mDirtyRawContacts.isEmpty()) {
2324a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2325a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2326a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mDirtyRawContacts);
2327a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2328a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2329a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2330a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2331b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
2332a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2333a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2334a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mUpdatedRawContacts);
2335a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2336a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2337b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2338b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2339b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
2340b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
23419d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
23429d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
23439d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
23449d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2345b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2346b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2347b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2348b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2349b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2350a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2351a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2352a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2353a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2354a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private void appendIds(StringBuilder sb, HashSet<Long> ids) {
2355b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2356a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2357b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2358a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2359a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2360285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2361285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2362285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2363cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
236481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
236581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
236681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
236781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
236881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
236981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
237081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2371cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2372568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
237351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
237451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mProviderStatus = status;
237551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.ProviderStatus.CONTENT_URI,
237651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                null, false);
237751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
237851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2379285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
2380ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return mInsertedRawContacts.containsKey(rawContactId);
2381285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2382285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
23833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
23843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
23853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
23863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
23873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
23883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
23893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
23903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
23913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
23924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2393de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2394bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
23951129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2396b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2397f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2398f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2399f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2400f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2401a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2402a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
240335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2404a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
240535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2406b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
240735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
240835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2409d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2410d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
24116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
24126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
24136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
24145ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2415dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                id = insertRawContact(uri, values, callerIsSyncAdapter);
2416f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2417a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2418a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2419a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24205ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
24215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2422f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2423f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2424a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2425a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2426a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2427a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2428f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2429f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2430a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2431a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2432a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2433ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2434f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2435f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2436ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2437ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2438ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2439eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
24405aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
244143880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2442eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2443eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2444eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
244582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
244682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
24471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
24481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
24491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2450a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
245181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2452f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2453a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2454a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
24567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
24577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2459de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2460a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2461a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2462a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2463e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2464e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2465e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2466e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2467e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2468e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2469e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2470e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2471e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2472e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2473e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2474e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2475e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
24767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2477e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2478f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2479f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2480e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2481f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2482f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2483f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2484e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2485e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2486e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2487e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2488e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
2489fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2490fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2491e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2492e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2493e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2494e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2495e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2496e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2497e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2498e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2499e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2500e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2501e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2502e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
2503fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2504fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2505e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2506e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2507e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2508f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2509f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2510e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2511f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2512f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2513e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2514e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2515f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2516f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2517e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2518f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2519f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2520f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2521f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2522035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2523f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2524e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
25257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
25267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2528d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
25296bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
25306bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
25316bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
25326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2533d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2534de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
25356bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
25366bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
25376bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2538a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2539a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2540f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2541f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2542dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
2543a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2544a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2545dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2546f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2547f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2548f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2549f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2550e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
25517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
25533d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2554f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
25553d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
25563d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2557f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2558f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
2559f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2560f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2561f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2562f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2563285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2564285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2565e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mInsertedRawContacts.put(rawContactId, account);
2566f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2567dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2568dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2569dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2570dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2571dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2572dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2573dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2574dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2575023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2576a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2577a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2578dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2579dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2580dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2581dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2582dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2583dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2584dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2585dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2586dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
2587dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
2588dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
2589dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2590dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2591dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2592dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2593dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2594dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2595dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2596dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2597dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2598dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2599dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2600dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2601dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2602dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2603dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2604dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2605dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2606dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2607dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2608dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2609dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2610dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2611dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2612dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2613dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2614dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2615dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2616dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2617dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2618dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2619dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2620dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2621dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2622dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2623dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2624dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2625dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2626dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2627dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2628dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2629dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2630a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2631a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2632a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2633a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2634a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2635a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2636f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2637a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2638de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2639de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
264067dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2641de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
264220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2643de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2644de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2645de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2646b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2647de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2648de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2649508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2650de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2651de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2652de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2653de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2654de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
26554097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2656b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2657de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2658a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2659a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2660a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2661f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2662de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2663a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2664b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2665a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
26664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
26674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
26688e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
26698e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
26708e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
26718e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
26728e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2673b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
2674f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
26758e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
26768e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
26778e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
26788e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
267969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
2680f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
26818e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
26828e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
26838e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
2684b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
2685f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
26868e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
26878e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
26888e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2689f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
26908e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2691f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2692c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
2693b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
26948e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
2695f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
26968e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2697f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2698f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2699f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2700a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
27015ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
27029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
27039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
27045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
27059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
27069261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
27079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
27089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
27099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
2710ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
2711ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Account account) {
2712ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2713ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (account == null) {
27144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
2715ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
27164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
2717ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            try {
2718ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                if (c.moveToFirst()) {
2719ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
2720ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
2721ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2722ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        account = new Account(accountName, accountType);
2723ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    }
27249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2725ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            } finally {
2726ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                c.close();
27279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
27289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2729ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
27309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
27319261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
2732ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    + "has a sourceid the the contact must be associated with "
27339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
27349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
27359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2736ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
2737ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (entries == null) {
2738ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            entries = new ArrayList<GroupIdCacheEntry>(1);
2739ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            mGroupIdCache.put(sourceId, entries);
2740ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2741ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2742ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int count = entries.size();
2743ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        for (int i = 0; i < count; i++) {
2744ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            GroupIdCacheEntry entry = entries.get(i);
2745ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) {
2746ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                return entry.groupId;
2747ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            }
2748ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2749ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2750ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        GroupIdCacheEntry entry = new GroupIdCacheEntry();
2751ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountName = account.name;
2752ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountType = account.type;
2753ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.sourceId = sourceId;
2754ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entries.add(0, entry);
2755ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
27569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
27575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
2758ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
27599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2760df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
27619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
2762ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (c.moveToFirst()) {
2763ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = c.getLong(0);
27649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
27659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2766df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2767df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
27689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
27699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
27709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
27719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
27729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
27739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2774ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = groupId;
27759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
27769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
27779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
27789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2779ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2780ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return entry.groupId;
27819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
27829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2783d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private interface DisplayNameQuery {
27841129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        public static final String RAW_SQL =
27851129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                "SELECT "
27861129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + DataColumns.MIMETYPE_ID + ","
27871129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.IS_PRIMARY + ","
27881129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.DATA1 + ","
27895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA2 + ","
27905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA3 + ","
27915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA4 + ","
27925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA5 + ","
27935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA6 + ","
27945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA7 + ","
27955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA8 + ","
27965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA9 + ","
27975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA10 + ","
27985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA11 +
27991129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " FROM " + Tables.DATA +
28001129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " WHERE " + Data.RAW_CONTACT_ID + "=?" +
28011129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        " AND (" + Data.DATA1 + " NOT NULL OR " +
28021129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                Organization.TITLE + " NOT NULL)";
2803d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2804d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int MIMETYPE = 0;
2805d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int IS_PRIMARY = 1;
28065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int DATA1 = 2;
28075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int GIVEN_NAME = 3;                         // data2
28085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int FAMILY_NAME = 4;                        // data3
28095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PREFIX = 5;                             // data4
28105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int TITLE = 5;                              // data4
28115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int MIDDLE_NAME = 6;                        // data5
28125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int SUFFIX = 7;                             // data6
28135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_GIVEN_NAME = 8;                // data7
28145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_MIDDLE_NAME = 9;               // data8
28155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int ORGANIZATION_PHONETIC_NAME = 9;         // data8
28165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_FAMILY_NAME = 10;              // data9
28175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int FULL_NAME_STYLE = 11;                   // data10
28185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int ORGANIZATION_PHONETIC_NAME_STYLE = 11;  // data10
28195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_NAME_STYLE = 12;               // data11
2820d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2821d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2822d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    /**
2823d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * Updates a raw contact display name based on data rows, e.g. structured name,
2824d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * organization, email etc.
2825d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     */
2826ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
2827bca1c8b44f99528fc123d5547723e44771e8e934Mike Lockwood        int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
28285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        NameSplitter.Name bestName = null;
28295dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String bestDisplayName = null;
28305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String bestPhoneticName = null;
28315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        int bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
2832d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
28331129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(rawContactId);
28341129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        Cursor c = db.rawQuery(DisplayNameQuery.RAW_SQL, mSelectionArgs1);
2835d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        try {
2836d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            while (c.moveToNext()) {
28371129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                int mimeType = c.getInt(DisplayNameQuery.MIMETYPE);
28385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                int source = getDisplayNameSource(mimeType);
28395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (source < bestDisplayNameSource || source == DisplayNameSources.UNDEFINED) {
28405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    continue;
28415dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                }
28421129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
28435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (source == bestDisplayNameSource && c.getInt(DisplayNameQuery.IS_PRIMARY) == 0) {
28445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    continue;
2845d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
28461129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
28475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (mimeType == mMimeTypeIdStructuredName) {
28485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    NameSplitter.Name name;
28495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (bestName != null) {
28505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name = new NameSplitter.Name();
28515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    } else {
28525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name = mName;
28535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name.clear();
28545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
28555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.prefix = c.getString(DisplayNameQuery.PREFIX);
28565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.givenNames = c.getString(DisplayNameQuery.GIVEN_NAME);
28575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.middleName = c.getString(DisplayNameQuery.MIDDLE_NAME);
28585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.familyName = c.getString(DisplayNameQuery.FAMILY_NAME);
28595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.suffix = c.getString(DisplayNameQuery.SUFFIX);
28605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.fullNameStyle = c.isNull(DisplayNameQuery.FULL_NAME_STYLE)
28615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            ? FullNameStyle.UNDEFINED
28625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            : c.getInt(DisplayNameQuery.FULL_NAME_STYLE);
28635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticFamilyName = c.getString(DisplayNameQuery.PHONETIC_FAMILY_NAME);
28645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticMiddleName = c.getString(DisplayNameQuery.PHONETIC_MIDDLE_NAME);
28655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticGivenName = c.getString(DisplayNameQuery.PHONETIC_GIVEN_NAME);
28665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticNameStyle = c.isNull(DisplayNameQuery.PHONETIC_NAME_STYLE)
28675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            ? PhoneticNameStyle.UNDEFINED
28685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            : c.getInt(DisplayNameQuery.PHONETIC_NAME_STYLE);
28695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (!name.isEmpty()) {
28705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayNameSource = source;
28715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestName = name;
28725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
28735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                } else if (mimeType == mMimeTypeIdOrganization) {
28745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    mCharArrayBuffer.sizeCopied = 0;
28755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.DATA1, mCharArrayBuffer);
28765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (mCharArrayBuffer.sizeCopied != 0) {
2877d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                        bestDisplayNameSource = source;
28781129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
28791129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
28805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticName = c.getString(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME);
28815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticNameStyle =
28825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                c.isNull(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME_STYLE)
28835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                    ? PhoneticNameStyle.UNDEFINED
28845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                    : c.getInt(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME_STYLE);
28855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    } else {
28865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        c.copyStringToBuffer(DisplayNameQuery.TITLE, mCharArrayBuffer);
28875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        if (mCharArrayBuffer.sizeCopied != 0) {
28881129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayNameSource = source;
28891129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayName = new String(mCharArrayBuffer.data, 0,
28901129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                    mCharArrayBuffer.sizeCopied);
28915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            bestPhoneticName = null;
28925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
28931129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        }
2894d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                    }
28955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                } else {
28965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    // Display name is at DATA1 in all other types.
28975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    // This is ensured in the constructor.
28985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
28995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    mCharArrayBuffer.sizeCopied = 0;
29005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.DATA1, mCharArrayBuffer);
29015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (mCharArrayBuffer.sizeCopied != 0) {
29025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayNameSource = source;
29035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
29045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
29055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticName = null;
29065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
29075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
2908d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
2909d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            }
2910d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2911d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        } finally {
2912d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            c.close();
2913d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        }
2914d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
29155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String displayNamePrimary;
29165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String displayNameAlternative;
29175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String sortKeyPrimary = null;
29185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String sortKeyAlternative = null;
29195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        int displayNameStyle = FullNameStyle.UNDEFINED;
29205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (bestDisplayNameSource == DisplayNameSources.STRUCTURED_NAME) {
29225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNameStyle = bestName.fullNameStyle;
29235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.CJK
29245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    || displayNameStyle == FullNameStyle.UNDEFINED) {
29255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.getAdjustedFullNameStyle(displayNameStyle);
29265dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bestName.fullNameStyle = displayNameStyle;
29275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
29285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29295dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNamePrimary = mNameSplitter.join(bestName, true);
29305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNameAlternative = mNameSplitter.join(bestName, false);
29315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            bestPhoneticName = mNameSplitter.joinPhoneticName(bestName);
29335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            bestPhoneticNameStyle = bestName.phoneticNameStyle;
29345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
29355dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNamePrimary = displayNameAlternative = bestDisplayName;
29365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
29375dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (bestPhoneticName != null) {
29395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyPrimary = sortKeyAlternative = bestPhoneticName;
29405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (bestPhoneticNameStyle == PhoneticNameStyle.UNDEFINED) {
29415dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bestPhoneticNameStyle = mNameSplitter.guessPhoneticNameStyle(bestPhoneticName);
29425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
29435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
29445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.UNDEFINED) {
29455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.guessFullNameStyle(bestDisplayName);
29465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (displayNameStyle == FullNameStyle.UNDEFINED
29475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        || displayNameStyle == FullNameStyle.CJK) {
29485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    displayNameStyle = mNameSplitter.getAdjustedNameStyleBasedOnPhoneticNameStyle(
29495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            displayNameStyle, bestPhoneticNameStyle);
29505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                }
29515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.getAdjustedFullNameStyle(displayNameStyle);
29525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
2953ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao            if (displayNameStyle == FullNameStyle.CHINESE ||
2954ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                    displayNameStyle == FullNameStyle.CJK) {
29555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                sortKeyPrimary = sortKeyAlternative =
2956cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao                        ContactLocaleUtils.getIntance().getSortKey(
2957ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                                displayNamePrimary, displayNameStyle);
29585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
29595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
29605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (sortKeyPrimary == null) {
29625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyPrimary = displayNamePrimary;
29635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyAlternative = displayNameAlternative;
29645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
29655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        setDisplayName(rawContactId, bestDisplayNameSource, displayNamePrimary,
29675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameAlternative, bestPhoneticName, bestPhoneticNameStyle,
29685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                sortKeyPrimary, sortKeyAlternative);
2969d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2970d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
29711129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private int getDisplayNameSource(int mimeTypeId) {
29721129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        if (mimeTypeId == mMimeTypeIdStructuredName) {
29731129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.STRUCTURED_NAME;
29741129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdEmail) {
29751129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.EMAIL;
29761129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdPhone) {
29771129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.PHONE;
29781129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdOrganization) {
29791129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.ORGANIZATION;
29801129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdNickname) {
29811129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.NICKNAME;
29821129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else {
29831129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.UNDEFINED;
29841129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        }
29851129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    }
29861129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
29879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
298820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
298920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
2990f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
299120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
299220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2993de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
2994de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
299514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
2996de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
2997de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
299814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
299914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
3000a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
3001a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
3002f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
300388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
300488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
300520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
300620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
3007de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
300820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
300920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
301020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
301120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
301220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
301388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
301488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
301588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
301620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
3017f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
301888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
301988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
30204da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
30214da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=?",
30224da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
3023f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
302420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
302520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
302620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
302720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
302820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
302914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
303020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
303120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
303220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
303320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
303420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
303520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
303620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
303720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
303820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
30397a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
304020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
304120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
304220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3043a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
3044813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return rowHandler.delete(mDb, c);
304520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
304620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
304720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
304820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
304920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
305020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
3051ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
3052ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
3053f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
3054f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
3055f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
3056f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
3057e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
3058ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3059ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
3060f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
306167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
3062f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
306367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
3064f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
3065ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3066dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
3067dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
3068dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
3069dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3070f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
3071f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
307273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
307373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3074f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
3075ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3076dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
3077dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
3078dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
3079dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
3080dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
3081dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
3082dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
3083dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
3084dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3085dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
3086dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
3087dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
3088dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3089dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
3090dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
3091dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
3092dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
3093dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (c.getLong(1) != 0) {
3094dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    final long rawContactId = c.getLong(0);
3095dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    insertDataGroupMembership(rawContactId, result);
3096dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    setRawContactDirty(rawContactId);
3097dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3098dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3099dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3100dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3101f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
31021a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3103ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
3104ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3105ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
3106ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3107ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31085aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
3109e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
31105aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
31111a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
31121a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3113e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
31141a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
3115e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
3116e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3117e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3118ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
311982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
31201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
312182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
312282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
31230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
31244dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
31254dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
31260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
312782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
31284dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
31294dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
31304dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
31314dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
31321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
31331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3134dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3135dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
313682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
3137f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
31382526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3139dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3140dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3141dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
31422526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
31432526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
31441f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3145dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3146dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
31470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
31480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
31490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
31500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3151dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3152dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3153dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
31542526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mMimeTypeIdIm);
3155dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
31562526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mMimeTypeIdEmail);
3157f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3158f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3159f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3160f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3161f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3162f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3163f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
31642526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
31652526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
31662526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
31672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
31682526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
31692526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
31702526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
31712526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3172dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
31732526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
31742526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3175dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
31762526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
31772526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3178dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
31792526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
31802526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
31812526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
31822526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
31832526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
31842526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3185dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
31862526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
31872526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3188dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3189dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
31901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
319182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
31922526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
31932526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3194dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
319570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
3196f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
319770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
31981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
31991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
3200de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
32012526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
32024394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
32031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
320467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
32055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
3206e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
32071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
32081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
32091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
32101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
32111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
321231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
321331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
321431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
32151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
32161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
321782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3218a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3219a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3220a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3221a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3222a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3223a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3224a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
322582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3226a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3227a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
322882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
322982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
323082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
323182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
323282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3233a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
323482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
323582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3236aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3237aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
32381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3239a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
3240a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
3241a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3242e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
32430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
324482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
324582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
32460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
32470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
32480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
32490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
32500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
32510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
32520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
32530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
32540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
32550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
32560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
32570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3258a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
3259a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
3260a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
326182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
326282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
3263a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
3264a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
32655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindString(mStatusUpdateReplace, 3, status);
32665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindString(mStatusUpdateReplace, 4, resPackage);
32675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindLong(mStatusUpdateReplace, 5, iconResource);
32685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindLong(mStatusUpdateReplace, 6, labelResource);
3269a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
3270a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
3271a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3272a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
3273a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
32745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateInsert, 2, status);
32755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateInsert, 3, resPackage);
32765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusUpdateInsert, 4, iconResource);
32775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusUpdateInsert, 5, labelResource);
3278a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
3279a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
3280a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
32810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    long timestamp = System.currentTimeMillis();
3282a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
32835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateAutoTimestamp, 2, status);
3284a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
32855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateAutoTimestamp, 4, status);
3286a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
32870a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
32885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusAttributionUpdate, 1, resPackage);
32895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusAttributionUpdate, 2, iconResource);
32905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusAttributionUpdate, 3, labelResource);
32910a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
32920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.execute();
3293a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
3294e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3295e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3296bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3297a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
3298a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
3299a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
3300a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.execute();
3301a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3302a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3303a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
33041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
33051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
33064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3307de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3308bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3309b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3310b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3311b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3312f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3313f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3314508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3315508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
331635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3317b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
331835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3319b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
3320b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3321b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3322b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3323b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
3324b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3325cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3326cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3327cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3328cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3329cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3330d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3331d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3332dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
33336bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
33346bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
33359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
33362e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
33372e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
33382e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3339fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3340fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
33412e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
33422e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
33432e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3344dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
33452e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
33462e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
33479fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
33489fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
33499fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
33509fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
33519fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
33529fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3353a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
33549fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
33559fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
33569fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
33579fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
33589fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
33599fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
33609fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
33619fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
336260de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
33639fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
33649fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
33659fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
33669fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
33679fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
33689fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3369dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
33709fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
33719fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
33729fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
33739fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
33749fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
33759fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
33769fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
33779fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
33789fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
33799fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
33802971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
33812971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
3382fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
3383fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3384e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
33852971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
33862971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
33872971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3388fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3389fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3390fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
33912971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
33922971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
33932971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
33942971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
33952971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
33962971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
33972971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
33985ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
33992971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
3400fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
3401fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3402508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3403508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
340420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3405f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3406944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3407f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
340820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
340920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
341048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
341148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
341248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
341348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3414508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3415f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
34164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
34174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3418ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3419ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3420ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3421f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
34225aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
34232971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
34242971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
34252971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
34262971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
34272971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
3428e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
34292971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
34302971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
34315aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
34322971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
34332971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
34342971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
34352971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
343681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3437f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
343881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
34392971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3440508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3441508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3442eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
344343880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3444e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3445eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3446eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
344782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
34480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
34491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
34501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
345181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
345281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
34533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
345481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3455508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
34564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
34574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
34581c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3459ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3460b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
346194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
3462de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
346394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
346494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
346594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
346694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3467f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
3468de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
346994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
347094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
347194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3472f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
3473de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
347494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
347594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
34761a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
347794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
347894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
347994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
34805aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
3481e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
34821a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3483e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3484e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3485e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3486dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
348796b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
3488cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
348996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
349096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3491cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3492cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3493cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3494dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3495cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3496cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3497cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3498cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3499cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3500cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3501cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3502cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3503fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
35043389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
3505f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
350614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
3507fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
3508fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
3509fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
351033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
3511b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
3512dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
351333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
351433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
351533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
35160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
35179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
35189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
35199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
35209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
35219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
35229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
35239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
35249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
35250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
35260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3527dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
352881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
352981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3530cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3531cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3532cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3533cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3534cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3535cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3536dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3537cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3538cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
35394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3540de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3541de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3542bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3543b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3544b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3545b5a4add17815167d20a90645779df34cdf45280dFred Quintana
354635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
354700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
354800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3549b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3550b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
35511129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3552b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
3553b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3554b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3555b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3556f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3557f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
355800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
355935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3560b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3561b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3562b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3563b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3564b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3565b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3566b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3567b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3568b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3569b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3570b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
357135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3572d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3573dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
357400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
357500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
357600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3577d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3578dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3579c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3580c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3581c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
35822e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
35832e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
35842e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
35852e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
35862e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3587fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3588fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
35892e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
35902e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
35912e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3592dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
35932e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
35942e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
35952e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
35967d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
35977d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
35987d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
35997d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
36007d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
36017d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
36027d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
36037d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
36047d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
36057d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
360620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3607944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3608f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
360981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3610f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
361181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
361220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
361320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3614c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
361548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
361648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
361748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
361848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3619f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
362081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3621f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
362281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
362300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
362400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
36257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
36265ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
36275ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3628dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
36297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
36307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
36317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
36325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
363333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
36344529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
36354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
36364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3637dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3638dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
36394529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
36404da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3641dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3642dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
36434529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
36447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
36457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
36467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3647ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
36485aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3649f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
365081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3651f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
365281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3653ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3654ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3655ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3656ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3657ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
36584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
36594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
366073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
36615aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
36625aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
366381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3664f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
366581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3666ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3667ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3668ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3669127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3670de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3671b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3672b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3673b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3674eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3675e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3676e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
367743880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3678eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3679eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3680eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
36819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
36829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
36839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
36849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
36859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
368672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
368772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                mContactDirectoryManager.scheduleDirectoryUpdateForCaller();
368872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
3689d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3690d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3691d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
369281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
369381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3694f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
369581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
369600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
369700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
369800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
36994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
37004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
37019705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
37029705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
37039705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
37049705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
37059705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
37069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
37079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
37089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
37099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
37109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
37119705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
37129705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
37139705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37149705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
37159705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
37169705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
37179705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
37189705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
37199705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
37209705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
37219705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
37229705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
37239705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37249705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37259705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
37269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
37279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
37289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
37299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
37309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
37319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
37329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
37339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
37349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
37379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
37389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
37399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
37409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
37419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
37429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
37439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
37449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
37459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
37469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
37479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
37489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
37499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
37529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
37539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
37549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3755aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3756aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
37579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
37589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37605aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3761f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
376273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3763ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3764ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
376573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3766f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
376773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
376873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
376973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
377073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
377173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
377273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
377373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
377473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3775ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
37761a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
37771a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
377894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
37796ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
37801129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
37816ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3782e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
37836ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
37846ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
37856ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
37866ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
37876ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
37886ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
37896ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
37906ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
37916ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3792ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
37936ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
37946ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
37956ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
37966ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
37976ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
37986ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
37996ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
38006ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
380194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
380294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
380394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3804b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3805b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3806e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
38071a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
38081a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3809e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3810e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3811e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3812e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3813dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
3814dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
38154529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
38164529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
38174529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
38184529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
381973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
382097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
382197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
382297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
382397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
382497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
38254529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3826b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
382751bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
38284529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
38294529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
38304529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
38314529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
3832dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
38334529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
38344529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
38354529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
38364529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
38374529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
38384529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
38394529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
38404529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
38414529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
3842dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
3843dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
384496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
384596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
384619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
384719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
384819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3849ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3850ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
385119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
385219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
385396b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
385419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
385519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
385619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3857ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3858ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
385919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
386019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
386119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
386219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
386319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
386419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
386519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3866f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
386796b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
38685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3869f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3870f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3871f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3872f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3873f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3874f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
387569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
3876f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3877f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3878433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
3879dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
3880dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3881dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
3882dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
38834529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3884dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3885dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
3886dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
3887dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
3888dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
3889dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3890dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
3891dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
3892dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
3893dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
3894dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3895dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3896dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3897dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
3898dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3899dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3900dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3901433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3902dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3903285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
39042b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3905285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3906f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3907f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3908f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3909f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3910f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
3911f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.bindLong(1, rawContactId);
3912f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.bindLong(2, rawContactId);
3913f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.execute();
3914f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3915f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3916f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
391719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
391819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                // undo delete, needs aggregation again.
3919ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                mInsertedRawContacts.put(rawContactId, new Account(accountName, accountType));
392019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
39215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
392333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
392433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3925321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3926f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
392720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
392820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
392920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
39305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
393120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
393220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
393320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
393420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
393520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3936b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
393720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
393820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
393970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
394070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
394120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
394220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
394320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
394470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
394520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
394670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
394720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
394870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
394920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
395070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
395120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
395220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
395397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
395497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
395597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
395697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
395797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
3958653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
395920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3960653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3961653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
396214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3963653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3964653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
3965f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
396620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3967653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3968653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
396920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
397020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3971653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
397220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
397320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3974f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
3975653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
3976653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
3977321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
3978653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
397914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
3980a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
3981813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        if (rowHandler.update(mDb, values, c, callerIsSyncAdapter)) {
3982813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
3983813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
3984813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
3985a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
3986321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
3987321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
39888c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
3989dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
39908c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
3991b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
39928c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
39938c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
39948c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
39958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
39968c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
3997dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
39988c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
39998c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
40008c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
40018c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
40028c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
40038c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
40048c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
40058c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
40068c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4007dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
4008dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
4009d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
40108c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4011b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
4012d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4013b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
4014d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4015b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
4016d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4017b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
4018d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4019b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
4020d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
4021d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4022d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
40238c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
4024d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
4025d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
4026d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
40278c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
4028c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
40298c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
4030c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
4031c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
40324da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
403397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
403497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
40358c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4036dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
4037dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
4038dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
4039dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
4040dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
4041dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4042dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4043dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4044dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4045dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4046dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4047dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4048dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4049dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4050dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
40518c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
40528c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
40538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4054b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
40558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4056b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
40578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4058b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
40598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4060b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
40618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4062b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
40638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
40648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
40659b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
40666e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
40679b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
40689b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
40699b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
40709b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
40719b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
40729b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4073f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4074d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4075127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4076127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
40770c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
40780c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
407980c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
40800c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
40810c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
40820c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
40830c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
40840c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
40850c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
40860c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4087b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4088127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
40890c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
40904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
40914da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
40920c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
40934da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
40944da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
40950c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
40966bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
40976bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
40980c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
40990c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
41000c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
41010c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4102127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4103127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
41043389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
410569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
410669cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
410769cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
410869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4109dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
4110b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId1 = mDbHelper.getContactId(rawContactId1);
41110c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
41120c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
4113b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId2 = mDbHelper.getContactId(rawContactId2);
41140c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
4115127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4116127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4117127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4118127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4119b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4120b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
412170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4122f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4123e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
4124627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
412570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
412670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
4127dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
4128743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
4129743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
4130743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
4131743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
4132e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
4133743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
4134743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
4135743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
4136743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4137743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
413848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4139627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
4140743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
4141627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
4142627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
4143627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
414470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
414570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
414633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
4147e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
4148e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
4149e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
4150e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
4151e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4152e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4153e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
4154e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
4155e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4156e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4157e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4158e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4159e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4160e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4161e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
4162e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4163e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4164e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4165e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
4166e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4167e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4168e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4169e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
4170e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4171e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4172e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
4173e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
4174d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
4175d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4176d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
4177d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
4178d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDirectoryCache = null;
4179e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4180e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
418133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
418233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4183e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
418433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
418533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
418633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
418733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
418869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
418969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
419069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
419133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
419233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
419369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
419469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
419533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
419633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
419733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
419833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
419933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
420033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
420133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
420233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
420333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
420433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
420533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
4206e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
420733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
420833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
4209e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
4210e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
4211e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
421270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
421370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
421470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
421570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
421673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
421770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4218619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
421972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
422072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager.onPackageChanged(packageName);
4221d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4222d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4223619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
4224627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
4225627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
4226dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
4227743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
4228743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
4229743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
4230627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4231627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
4232dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
4233627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
4234627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4235627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4236627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4237627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4238627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
4239627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4240627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4241627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    /**
4242622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
4243622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
424467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
424567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
424667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
424767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
424867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
424967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
425067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
425167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
425267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
425367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
425467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
425567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
4256dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
4257622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
4258dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
4259dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
4260622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
4261622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
4262dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
4263622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
4264622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
42654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
42664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
42674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
4268d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4269d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directory == null || directory.equals("0")) {
4270d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder, false);
4271d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
4272d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder, true);
4273d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4274d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4275d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4276d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4277d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            throw new IllegalArgumentException(
4278d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDbHelper.exceptionMessage("Invalid directory ID", uri));
4279d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4280d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4281d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4282d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4283d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4284d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4285d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4286d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4287d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4288d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4289d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4290d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4291d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
429209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
429309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
429409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
429509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
429609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4297d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        return getContext().getContentResolver().query(directoryUri, projection, selection,
4298d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
4299d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4300d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4301d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4302d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4303d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4304d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4305d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4306d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4307d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4308d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4309d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4310d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4311d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4312d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4313d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4314d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4315d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4316d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4317d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4318d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
4319d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (mDirectoryCache == null) {
4320d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            mDirectoryCache = new HashMap<String, DirectoryInfo>();
4321d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            Cursor cursor = mDb.query(Tables.DIRECTORIES,
4322d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    DirectoryQuery.COLUMNS,
4323d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    null, null, null, null, null);
4324d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            try {
4325d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                while (cursor.moveToNext()) {
4326d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    DirectoryInfo info = new DirectoryInfo();
4327d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
4328d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
4329d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
4330d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
4331d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDirectoryCache.put(id, info);
4332d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
4333d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            } finally {
4334d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                cursor.close();
4335d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4336d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4337d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4338d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        return mDirectoryCache.get(directoryId);
4339d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4340d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
434172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
434272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mDirectoryCache = null;
434372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
434472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
4345d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    public Cursor queryLocal(Uri uri, String[] projection, String selection, String[] selectionArgs,
4346d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                String sortOrder, boolean hiddenOnly) {
4347bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4348bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4349bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
43500b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4351b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
435235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4353d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
43541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4355c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4356c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4357619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
4358619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
4359a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
43604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
436135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4362b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
436335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
436435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4365d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4366763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4367d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                if (hiddenOnly) {
4368d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    qb.appendWhere(Contacts.IN_VISIBLE_GROUP + "=0");
4369d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
4370619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4371619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4372619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4373d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
43744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4375763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
43764da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
43774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
43786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
43796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
43806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
43815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
43825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
43835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
43845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
43855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4386fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4387fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
43885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4389a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
43905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
43915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
43925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
43935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4394763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4395a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4396a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4397a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4398a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4399a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
44005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
44015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
44025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
44035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4404763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
44054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
44064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
44074da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
44085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
44095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
44105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
44112149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
44122149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
44132149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
44142149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
44152149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
44162149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
44172149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
44182149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
44192149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
44202149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
44212149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
44222149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
44232149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
4424a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4425a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4426a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4427a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
4428a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
44292149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
44302149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
44312149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
44322149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
44332149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
44342149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
44352149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44362149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
44372149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
44382149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
44392149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
44402149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
44412149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
4442f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4443f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
444442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
4445763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
4446f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
44474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
44484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
44494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4450f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4451f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4452f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
445342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
445442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
445542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
445642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
445742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
445842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
445942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
446042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
446142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
446242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4463ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4464916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4465ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4466916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4467ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
4468916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(qb, uri, projection, filterParam);
4469d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                if (hiddenOnly) {
4470d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    qb.appendWhere(Contacts.IN_VISIBLE_GROUP + "=0");
4471d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
4472ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4473ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4474ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4475ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4476ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
44774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
4478ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
4479d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
44804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
44814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4482e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
44835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
44844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
44854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
44864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4487763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4488ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
44895e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
44905e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
44915e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
4492dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    starredProjection =
4493dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
4494dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    frequentProjection =
4495dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
44965e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
44975e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
44984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
44994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
45004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4501d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
45025e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
45035e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
45044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4505d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4506d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
4507d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
4508763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
45094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
45104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4511d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
45125e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
45135e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
4514d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
4515d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
45164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4517d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4518d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
4519d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
4520d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
45214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
45224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
4523d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
4524d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4525d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
4526d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
4527d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4528d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4529ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4530763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4531b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
453271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
45334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4534b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4535b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4536b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4537b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
4538a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
45394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
454082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
45424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
45436bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
45446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
454500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4546a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
45473653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
454882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45494da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
45504da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
45513653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
45523653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
45533653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
45543653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4555a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
4556a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4557a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4558a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
4559a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
4560a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4561a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4562a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4563a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
4564a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
4565a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
4566a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
4567a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
4568a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4569a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
4570a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4571a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
4572a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
4573a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4574a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4575a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
4576a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4577a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4578a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4579a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4580a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
4581a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
4582a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
4583a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
4584a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
4585a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4586a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4587a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4588a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
4589a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
4590a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
4591a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4592a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4593a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
45944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
459582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
459689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
45972815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
45982815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
45992815f58f72f109790585931f601a63ddc02536a5Evan Millar
460048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
460182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46024da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
460348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
46044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
460548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
460648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
460748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4608ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
460982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
461089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
4611ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
46124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
46134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4614a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
46155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
461645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
46175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
46185e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
46195e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
46205e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
46217318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
46225e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
462345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
46245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
46255e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
46265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (isPhoneNumber(filterParam)) {
46275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
46285e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
46295e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
46305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String number = PhoneNumberUtils.convertKeypadLettersToDigits(filterParam);
46315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        String reversed = PhoneNumberUtils.getStrippedReversed(number);
46325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
46335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                " IN (SELECT " + PhoneLookupColumns.DATA_ID
46345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " FROM " + Tables.PHONE_LOOKUP
46355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                                  + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '%");
46365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(reversed);
46375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append("')");
463845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
463945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
464045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
464145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
464245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
464345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
464445d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
46455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
46465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4647a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
4648ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
46495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
4650a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4651a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
4652a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
4653ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4654ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4655ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
46564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
465782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
465889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
46594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
46604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
46614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
466248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
466382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46644da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
46654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
46664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
466748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
466848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
466948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
46705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
467182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
467289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
46734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
467408768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
467508768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
467608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
467708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
46784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
4679ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4680ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4681ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
46825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
468382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
468407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
468507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
468607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
468707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
468807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
468907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
469007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
46915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
469207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
469307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
469407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
469507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
469607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
469707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
469807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
469907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
470007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
470107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
470207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " AND " + Data.DATA1 + " LIKE ");
470307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
470420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
470520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
470620938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
470707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
470807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            /*
470907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * Using a UNION instead of an "OR" to make SQLite use the right
471007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * indexes. We need it to use the (mimetype,data1) index for the
471107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * email lookup (see above), but not for the name lookup.
471207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * SQLite is not smart enough to use the index on one side of an OR
471307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * but not on the other. Using two separate nested queries
471407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * and a UNION between them does the job.
471507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             */
471607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            sb.append(
471707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " UNION SELECT " + Data._ID +
471807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " FROM " + Tables.DATA +
471907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " WHERE +" + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
472007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " AND " + Data.RAW_CONTACT_ID + " IN ");
47217318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
472220938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
47235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
47245e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4725a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
47265e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
47275e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
4728a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4729a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
4730a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
47315e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
47325e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
47335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4734ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
473582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
473689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
473789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4738ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4739ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4740ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
474148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
474282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
47434da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
474448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
474548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
47464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
474748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
474848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
474948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
47505ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4751763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
47524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
47534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
47544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
47555ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
47565ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
4757763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
47584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
47594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
47604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
47614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
47624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
47635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
47645ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
476582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
47664da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
47674da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
4768e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4769e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4770e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
4771e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
477282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
4773e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4774e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4775e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
47764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
477782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
47784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
47794da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
4780a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
4781a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
4782a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
4783a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
47844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4785a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
4786a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
4787a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
4788e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                    sortOrder = RawContactsColumns.CONCRETE_ID;
4789a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
4790a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4791e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
4792b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, number);
4793e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
4794e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov
4795e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
4796e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
4797e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
4798a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
4799a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
4800a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4801ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
4802b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4803ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
480489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4805ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4806ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4807ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4808ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
4809b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4810ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
48114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
48124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
4813ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4814ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4815ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4816ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
4817b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
4818ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
481989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
482089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
4821ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4822ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4823ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4824b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
48250c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
4826b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
4827b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
4828b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
4829b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
483031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
4831d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
48322d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
48332d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
48342d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
48352d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
483631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
4837d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
4838d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
483931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
484031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
484131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
484231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4843763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
48447581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
48457581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
48462d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                        maxSuggestions, filter);
484731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
484831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4849eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
4850eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
4851eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
485289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4853e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4854e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
4855e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
4856b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
4857e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
485882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4859b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
4860e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4861e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
486282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4863b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
4864e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4865e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
4866e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4867eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
4868eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
4869eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
487082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
48710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
48725ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
48735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
48745ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
487582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
48760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
48774da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
48784da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
48795ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
48805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
48815ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
4882c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
4883a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
4884c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4885c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4886c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
48872d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
48882d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, lookupKey, projection);
4889c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4890c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
48911b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
4892b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
48931b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
48941b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
48951b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
48961b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
4897b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
48981b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
48991b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
49001b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
49011b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
49021b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
4903b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
49041b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
49051b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
49061b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
49071b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
49081b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
4909b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
49101b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
491171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
49121b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
49131b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
49141b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
491546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
4916a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
491746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
491846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
491946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
492046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
492146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
4922a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
49234da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
49244da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
492546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
492646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
492746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
492809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
492909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
493009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
493109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4932d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
4933d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4934d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4935d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4936d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4937d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4938d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
4939d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                long directoryId = ContentUris.parseId(uri);
4940d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4941d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4942d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(directoryId));
4943d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
4944d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4945d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4946d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
49474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
4948f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
4949c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
49504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
49514f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
49527f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov        qb.setStrictProjectionMap(true);
49537f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
4954ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
4955ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
4956ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
4957ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
4958ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
4959ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
49605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
49615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
49625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
49635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
49645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
4965038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
4966038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
4967038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
4968038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
49695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
49705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
49714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
49724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
49734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
49744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
49754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
49764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
497709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
497809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
497909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
498009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
498109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
498209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
498309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
498409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
498509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
498609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
498709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
498809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
498909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
499009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
499109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
499209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4993a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
4994a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
4995a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
4996a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
4997a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
4998a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
4999a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5000a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5001a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5002a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5003a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5004a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5005a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5006a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5007a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5008a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5009a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5010a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5011a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5012a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5013a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
5014a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
5015a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5016a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5017a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5018a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5019a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5020a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5021a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
502209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5023bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
5024bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
5025bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
5026bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
5027ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5028bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
5029bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
5030ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
5031ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5032bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
5033bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
5034bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
5035bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5036de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
5037ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5038ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5039ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
5040ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
5041ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
5042ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
5043ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
5044ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
5045ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
5046ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5047ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
5048ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
5049ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
5050ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
5051ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
5052ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
5053ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
5054ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
5055ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
5056ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
5057ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
5058ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5059ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
5060ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
5061ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5062ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5063bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
5064ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
5065bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
5066bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "SUBSTR(" + sortKey + ",1,1) AS " + AddressBookIndexQuery.LETTER);
5067bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5068bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
5069bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
5070bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
5071bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
5072bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
5073bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
5074bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
5075ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
5076bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "GET_PHONEBOOK_INDEX(SUBSTR(" + sortKey + ",1,1),'" + locale + "')"
5077bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
5078ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
5079ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
5080ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
5081ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5082f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
5083ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
5084ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
5085ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5086ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
5087f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
5088ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
5089ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
5090bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
5091bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
5092bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5093bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
5094bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
5095bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
5096ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
5097f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
5098bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
5099bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
5100bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
5101bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
5102bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
5103bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
5104bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
5105bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
5106bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
5107bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
5108bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5109bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
5110bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
5111bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
5112bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
5113bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5114bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
5115bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
5116bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
5117ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5118ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5119ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            final Bundle bundle = new Bundle();
5120ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
5121f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
5122ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            return new CursorWrapper(cursor) {
5123ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5124ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                @Override
5125ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                public Bundle getExtras() {
5126ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                    return bundle;
5127ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                }
5128ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            };
5129ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
5130f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
5131ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5132ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5133ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
51342d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
513592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
513692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
513792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
513892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
51392d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
51402d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
51415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
51425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
51435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
514492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
514592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
514692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
514792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
514892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
514992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
515092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
515192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
515292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
515392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
515492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
515592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
515692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
515792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
515892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
515992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
516092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
516192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
516292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
51635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
51645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
51655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
51675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
51685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
51705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
51715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
51735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
51745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
51755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
51765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
51775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
51785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
51805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
51815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
51825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
51835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
51845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
51865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
51875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
51885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
51895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
51905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
519192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
51925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
51935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
51945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
51955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
51965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
51975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
51985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
51995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
52005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
52015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
52025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
52035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
52045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
52055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
52065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
52075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
52085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
52095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
521092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
521192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
52125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
52135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
52145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
52155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
52165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
52175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
52185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
52195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
52205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
52235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
52245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
522592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
522692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
52275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
52295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
52305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
52315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
523292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
52335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
52345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
52365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
52375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
523892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
52395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
52405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
524192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
524292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
524392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
524492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
52455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
52465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
524792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
524892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
524992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
52505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
52515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
525292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
525392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
52545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
525592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
525692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
525792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
525892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
525992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
526092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
526192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
526292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
526392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
526492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
526592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
526692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
526792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
526892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
526992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
527092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
527192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
527292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
527392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
527492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
527592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
52765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
527892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
527992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
528092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
528192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
528292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
528392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
528492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
528592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
528692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
528792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
528892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
528992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
529092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
529192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
529292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
529392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
529492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
529592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
529692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
529792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
529892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
52995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
53005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
53015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
53025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
530392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
530492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
53055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
53065870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
53075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
53085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53095870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
53105870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
53115870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
53125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
53145870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
53155870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
53165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
53175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
53185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
53195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
53205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
53215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
53225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
53235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
532492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
532592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
532692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
53275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
53285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
53295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
53305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
53315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
53325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
53335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
53345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
53355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
53385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
53395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
534092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
534192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
534292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
534392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
534492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
534592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
534692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
534792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
534892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
534992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
535092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
5351ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
5352ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
5353ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
5354ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
53555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
53565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
53575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
53585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
53595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
53605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
53625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
53635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
53655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
53665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
53685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
53695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
53705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
53715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
53725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
53735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
53745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
53755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
53765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
53775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
53785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
53795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
53805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
53815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
53825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
53845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
53855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
53865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
53875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53885870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
53895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5390763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5391763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
539282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5393916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5394916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5395916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
5396916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5397916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5398916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
5399916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
5400916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
5401916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
5402916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
5403916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            String[] projection, String filter) {
5404916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5405916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5406916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5407916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5408916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(" JOIN (SELECT " +
5409916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                RawContacts.CONTACT_ID + " AS snippet_contact_id");
5410916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5411916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA_ID)) {
5412916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", " + DataColumns.CONCRETE_ID + " AS "
5413916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    + SearchSnippetColumns.SNIPPET_DATA_ID);
5414916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5415916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
54169c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA1)) {
54179c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA1 + " AS " + SearchSnippetColumns.SNIPPET_DATA1);
5418916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5419916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
54209c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA2)) {
54219c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA2 + " AS " + SearchSnippetColumns.SNIPPET_DATA2);
5422916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5423916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
54249c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA3)) {
54259c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA3 + " AS " + SearchSnippetColumns.SNIPPET_DATA3);
54269c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
54279c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov
54289c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA4)) {
54299c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA4 + " AS " + SearchSnippetColumns.SNIPPET_DATA4);
5430916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5431916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5432916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_MIMETYPE)) {
5433916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", (" +
5434916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    "SELECT " + MimetypesColumns.MIMETYPE +
5435916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " FROM " + Tables.MIMETYPES +
5436916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " WHERE " + MimetypesColumns._ID + "=" + DataColumns.MIMETYPE_ID +
5437916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    ") AS " + SearchSnippetColumns.SNIPPET_MIMETYPE);
5438916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5439916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5440c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS + " WHERE ");
5441c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
5442c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        if (!TextUtils.isEmpty(filter)) {
5443c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append(DataColumns.CONCRETE_ID + " IN (");
5444c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
5445c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            // Construct a query that gives us exactly one data _id per matching contact.
5446c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            // MIN stands in for ANY in this context.
5447c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append(
5448c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    "SELECT MIN(" + Tables.NAME_LOOKUP + "." + NameLookupColumns.DATA_ID + ")" +
5449c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    " FROM " + Tables.NAME_LOOKUP +
5450c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    " JOIN " + Tables.RAW_CONTACTS +
5451c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    " ON (" + RawContactsColumns.CONCRETE_ID
5452c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                            + "=" + Tables.NAME_LOOKUP + "." + NameLookupColumns.RAW_CONTACT_ID + ")" +
5453c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    " WHERE " + NameLookupColumns.NORMALIZED_NAME + " GLOB '");
5454c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append(NameNormalizer.normalize(filter));
5455c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
5456c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                        " IN(" + CONTACT_LOOKUP_NAME_TYPES + ")" +
5457c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    " GROUP BY " + RawContactsColumns.CONCRETE_CONTACT_ID +
5458c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    ")");
5459c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        } else {
5460c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append("0");     // Empty filter - return an empty set
5461c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        }
5462c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
5463c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
5464916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5465916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5466916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
5467916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5468916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5469916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void appendContactsTables(StringBuilder sb, Uri uri, String[] projection) {
5470763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5471f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5472763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5473763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5474d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5475763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5476763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
5477a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
5478a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
547982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
5480ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
5481763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
5482763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
5483763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5484f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5485763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5486763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5487d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5488763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5489763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
5490763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
5491763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
5492763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
5493763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
5494763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5495a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
5496a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(mDbHelper.getRawEntitiesView(shouldExcludeRestrictedData(uri)));
5497a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
549846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
549946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
550046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
550182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
550282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
550382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5504a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getDataView(shouldExcludeRestrictedData(uri)));
550582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
550682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
5507a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
5508a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5509a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5510a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
55113296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
551282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
551382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);
551482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
5515ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
5516ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
55170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
55180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
55190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5520b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
55210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
5522a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5523a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
55240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5525a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5526a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
5527a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5528a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5529a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
5530a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
5531a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5532a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getEntitiesView(shouldExcludeRestrictedData(uri)));
5533a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
5534a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5535a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
5536a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5537a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
5538a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
5539a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5540a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5541a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
5542a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
5543a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5544a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5545a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
5546a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
5547a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5548a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
5549a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
5550a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
5551a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
5552a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
5553a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
5554a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
5555a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
5556a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
55570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5558a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
55590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5560a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
5561a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5562b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
55630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
55640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
55650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
55660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
55670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
55680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
5569a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
5570a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
55710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5572a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5573a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5574a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
5575a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
5576a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5577a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
5578a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
5579a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
5580a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
5581a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5582a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5583a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5584a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
5585a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5586a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
5587a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
5588a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
5589a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5590a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5591a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5592a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private boolean shouldExcludeRestrictedData(Uri uri) {
5593a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        // Note: currently, "export only" equals to "restricted", but may not in the future.
5594a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
5595a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.FOR_EXPORT_ONLY, false);
5596a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (excludeRestrictedData) {
5597a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return true;
5598a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5599a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5600a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5601a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5602a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (requestingPackage != null) {
5603a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5604a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5605a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5606a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return false;
56070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
56080a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
56094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
5610f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5611f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5612e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5613e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5614e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5615e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5616fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5617fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5618e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5619e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5620e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5621e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5622e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5623e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
56244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
56254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
56264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
56274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
56284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
56294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
56304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
56314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
56324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5633e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
5634f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5635f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5636e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5637e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5638e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5639e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5640fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5641fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5642e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5643e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5644e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5645e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5646e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5647e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
5648e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
5649e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
5650e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
5651e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
5652e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
5653e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
5654e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
5655e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
5656e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
5657e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
5658e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
5659e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
5660e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
5661e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
5662e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
56637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5664c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
5665c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
5666c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
5667c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
5668c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
5669f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
5670f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String limitParam = getQueryParameter(uri, "limit");
5671c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
5672c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5673c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5674c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
5675c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
5676c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
5677c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
5678c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
5679c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
5680c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5681c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
5682c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
5683c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
5684c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5685c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5686c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5687c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
56885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /**
56895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * Returns true if all the characters are meaningful as digits
56905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     * in a phone number -- letters, digits, and a few punctuation marks.
56915e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov     */
56925e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private boolean isPhoneNumber(CharSequence cons) {
56935e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        int len = cons.length();
56945e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
56955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        for (int i = 0; i < len; i++) {
56965e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            char c = cons.charAt(i);
56975e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
56985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= '0') && (c <= '9')) {
56995e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
57005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
57015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
57025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    || (c == '#') || (c == '*')) {
57035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
57045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
57055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'A') && (c <= 'Z')) {
57065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
57075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
57085e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            if ((c >= 'a') && (c <= 'z')) {
57095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                continue;
57105e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
57115e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
57125e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            return false;
57135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        }
57145e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
57155e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        return true;
57165e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
57175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
571800ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
5719d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
572070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
572170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
5722fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
572370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
572470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
572570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
572670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
5727d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
572870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
572967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
57305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
57315ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
5732619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
5733619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
5734619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5735b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
5736b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
5737b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
5738b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
5739a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
5740e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                return openPhotoAssetFile(uri, mode,
5741e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " + RawContacts.CONTACT_ID + "=?",
5742e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5743e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
5744b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5745e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
5746e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                return openPhotoAssetFile(uri, mode,
5747e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
57484da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5749d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5750d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5751f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
575242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
575342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                mSelectionArgs1[0] = String.valueOf(lookupContactIdByLookupKey(mDb, lookupKey));
575442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + "=?";
575542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
575642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
575742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
575842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
575942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
576042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                outputRawContactsAsVCard(localStream, selection, mSelectionArgs1);
576142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return buildAssetFileDescriptor(localStream);
576242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
576342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
576442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
576542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
576642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
576742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
576842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
5769d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
5770d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
577142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
577242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
5773d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
577442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
5775d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
577642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
5777d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                    inBuilder.append(lookupContactIdByLookupKey(mDb, lookupKey));
577842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
577942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
578042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
578142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
5782d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5783d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
5784d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
5785d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
5786d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5787d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                outputRawContactsAsVCard(localStream, selection, null);
5788d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                return buildAssetFileDescriptor(localStream);
5789d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5790b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5791b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
5792fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
5793fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
5794b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
5795b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
5796b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5797e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    private AssetFileDescriptor openPhotoAssetFile(Uri uri, String mode, String selection,
5798e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
5799e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
5800e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
5801e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
5802e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
5803e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
5804e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5805e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
5806e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
5807e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
5808e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
5809e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql,
5810e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                selectionArgs);
5811e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
5812e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5813d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
5814d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5815d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5816d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Build a {@link AssetFileDescriptor} through a {@link MemoryFile} with the
5817d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
5818d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5819d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private AssetFileDescriptor buildAssetFileDescriptor(ByteArrayOutputStream stream) {
5820d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        AssetFileDescriptor fd = null;
5821d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
5822d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
5823d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5824d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
5825d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final int size = byteData.length;
5826d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5827d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final MemoryFile memoryFile = new MemoryFile(CONTACT_MEMORY_FILE_NAME, size);
5828d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.writeBytes(byteData, 0, 0, size);
5829d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            memoryFile.deactivate();
5830b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5831d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            fd = AssetFileDescriptor.fromMemoryFile(memoryFile);
5832d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
5833d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            Log.w(TAG, "Problem writing stream into an AssetFileDescriptor: " + e.toString());
5834d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5835d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        return fd;
5836d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5837d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5838d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5839d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
5840d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
5841d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
5842d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5843d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
5844d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
5845d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
58467a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
58477a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa                new VCardComposer(context, VCardConfig.VCARD_TYPE_DEFAULT, false);
5848d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
5849d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5850f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
58517a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        if (!composer.init(selection, selectionArgs)) {
58527a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa            Log.w(TAG, "Failed to init VCardComposer");
5853d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
58547a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        }
5855d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5856d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
5857d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
5858d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
5859d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5860d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5861d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
5862d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5863b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
58644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
58654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
5866a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
58674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
5868b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
5869be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
58702d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
5871b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
5872b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
5873b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
5874f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
587542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
5876f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
5877f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
5878f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                return "image/png";
5879b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
5880be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
5881b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
5882b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
5883508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
5884b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
588548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
588648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
588748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
588848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
58899005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
58909005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
589148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
589248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
589348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
589448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
589548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
589648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
589748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
589848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
5899b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
5900b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
5901b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
5902b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
5903b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
5904b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
5905b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
5906b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
5907c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
5908c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
5909c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
5910c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
5911d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
5912d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
5913d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
5914d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
591561efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
591661efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
59174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
59184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
59197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
592009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
592109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
592209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
592309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
592409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
592509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
592609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
592709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
592809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
592909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
593009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
593109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
593209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
593309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
593409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
593509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
593609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
593709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
593809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
593909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
594009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
594109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
594209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
594309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
594409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
594509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
594609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
594709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
594809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
594909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
595009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
595109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
595209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
595309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
595409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
595509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
595609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
595709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
595809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
595909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
596009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
596109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
596209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
596309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
596409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
596509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
59665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void setDisplayName(long rawContactId, int displayNameSource,
59675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            String displayNamePrimary, String displayNameAlternative, String phoneticName,
59685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            int phoneticNameStyle, String sortKeyPrimary, String sortKeyAlternative) {
59695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(1, displayNameSource);
59705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 2, displayNamePrimary);
59715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 3, displayNameAlternative);
59725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 4, phoneticName);
59735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(5, phoneticNameStyle);
59745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 6, sortKeyPrimary);
59755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 7, sortKeyAlternative);
59765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(8, rawContactId);
597725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
59783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
59793cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
598073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
598173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
598273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
598373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
5984a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.add(rawContactId);
598573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
598673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
5987c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
5988c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
5989c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
5990c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
5991c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
5992c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
5993653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
5994c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
5995653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
5996653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
5997c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
5998c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
5999c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
6000c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
6001c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
6002c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
6003c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
6004c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
6005c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
6006653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
6007c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
6008653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
6009653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
6010c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
6011c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
6012ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
6013813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov    public String insertNameLookupForEmail(long rawContactId, long dataId, String email) {
6014f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
6015813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return null;
6016f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6017f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6018b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov        String address = mDbHelper.extractHandleFromEmailAddress(email);
6019b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov        if (address == null) {
6020813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return null;
6021f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6022f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6023f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
6024f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
6025813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        return address;
6026f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6027f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6028f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
6029f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
6030f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
6031f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
6032f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
6033f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
6034f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6035f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6036f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
6037f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
6038f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6039f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6040a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
6041a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
6042a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
6043a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
6044a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
6045a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
6046a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
6047a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
6048a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
6049a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
6050a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
6051f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6052d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name,
6053d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            int fullNameStyle) {
6054d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name, fullNameStyle);
6055f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6056f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6057f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
6058f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6059f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
6060f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
6061f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6062f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6063f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6064f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
6065f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
6066f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
6067f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6068f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6069f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6070f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
6071d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
6072f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6073f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6074f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
607548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    public void insertNameLookupForPhoneticName(long rawContactId, long dataId,
607648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            ContentValues values) {
607748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (values.containsKey(StructuredName.PHONETIC_FAMILY_NAME)
607848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                || values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)
607948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                || values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME)) {
608048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId,
608148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
608248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
608348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
608448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
608548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    }
608648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
608748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    public void insertNameLookupForPhoneticName(long rawContactId, long dataId, String familyName,
608848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            String middleName, String givenName) {
608948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        mSb.setLength(0);
609048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (familyName != null) {
609148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(familyName.trim());
609248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
609348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (middleName != null) {
609448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(middleName.trim());
609548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
609648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (givenName != null) {
609748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(givenName.trim());
609848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
609948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
610048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (mSb.length() > 0) {
610148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookup(rawContactId, dataId, NameLookupType.NAME_COLLATION_KEY,
610248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    NameNormalizer.normalize(mSb.toString()));
610348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
61043b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov
61053b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov        if (givenName != null) {
61063b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            // We want the phonetic given name to be used for search, but not for aggregation,
61073b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            // which is why we are using NAME_SHORTHAND rather than NAME_COLLATION_KEY
61083b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            insertNameLookup(rawContactId, dataId, NameLookupType.NAME_SHORTHAND,
61093b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov                    NameNormalizer.normalize(givenName.trim()));
61103b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov        }
611148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    }
611248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
6113f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
6114f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
6115f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
6116f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
61175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(1, rawContactId);
61185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(2, dataId);
61195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(3, lookupType);
61205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mNameLookupInsert, 4, name);
6121f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
6122f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6123f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6124f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
6125f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
6126f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
6127f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
61285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupDelete.bindLong(1, dataId);
6129f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
6130f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6131f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
61322d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
6133d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
6134d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
6135d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
6136d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
6137d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
6138d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
6139d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
6140e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
6141916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
6142916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
6143e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
6144e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
61455ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
6146c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
61477318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam);
6148c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
6149c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6150c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
61517318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam) {
61527318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), true);
61535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
61545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
61555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
61567318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov            boolean allowEmailMatch) {
6157d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
6158dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov                "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
6159d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
6160d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
6161d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " GLOB '");
61625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
6163a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
6164a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NAME_COLLATION_KEY + ","
6165a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NICKNAME + ","
61664cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                + NameLookupType.NAME_SHORTHAND + ","
6167f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                + NameLookupType.ORGANIZATION + ","
616889f1f71495aedc58252b3f58a46a036986c319d2Dmitri Plotnikov                + NameLookupType.NAME_CONSONANTS);
616920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        if (allowEmailMatch) {
617020938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
617120938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        }
617289f1f71495aedc58252b3f58a46a036986c319d2Dmitri Plotnikov        sb.append("))");
6173ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
6174ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
61754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
61764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
61774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
61784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
6179b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
6180b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
6181b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
6182b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
6183b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
61844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
61854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
6186b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
6187b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
6188b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
6189caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
61905e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
61915e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
61925e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
61935e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
61945e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
61955e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
61965e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
61975e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
61985e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
61995e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
62005e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
6201caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
6202caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
6203caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
6204df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
6205df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
6206caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
6207caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
6208caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
6209caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
62106f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
6211caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
62126f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
6213caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
6214f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
621573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
621673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
621773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
621873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
6219bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
6220bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
6221bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
6222bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
622373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
622473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
622573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
622673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
622773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
6228627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
6229627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
6230627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
6231627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
623273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
623373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
623473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
6235627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
6236627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
6237627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
6238627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
6239627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
624073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
624173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
624273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
624373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
624473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
624573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
624673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
6247627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
6248b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
6249d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
6250f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
6251f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
6252f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6253f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
6254f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6255f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6256f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6257f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6258f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6259f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
6260f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
6261f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6262f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6263f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6264f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
6265f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6266f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
6267f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
6268f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6269f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6270f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
6271f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
6272f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
6273f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
6274f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
6275f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6276f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6277f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
6278f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
6279f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
6280f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
6281f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6282f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6283f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
6284f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6285f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6286f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
6287f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
6288f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6289f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
6290f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
6291f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
6292f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
6293f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
6294f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
6295f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6296f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6297f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
6298f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6299f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
6300f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
6301f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6302f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6303f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
6304f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
6305f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
6306f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6307f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6308f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6309f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
6310f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
6311f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
6312f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
6313f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
6314f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6315f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6316f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
6317f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
63185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
63195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void bindString(SQLiteStatement stmt, int index, String value) {
63205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (value == null) {
63215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindNull(index);
63225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
63235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindString(index, value);
63245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
63255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
63265dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
63275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void bindLong(SQLiteStatement stmt, int index, Number value) {
63285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (value == null) {
63295dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindNull(index);
63305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
63315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindLong(index, value.longValue());
63325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
63335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
63344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
6335