ContactsProvider2.java revision 2530512f639c4979fd7371c7dd25dd67e8118124
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;
205b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport com.android.providers.contacts.ContactAggregator.AggregationSuggestionParameter;
2197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
2297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
2397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
2497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
2597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
2697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsStatusUpdatesColumns;
2797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
2897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
2997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
3097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
3197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
3297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
3397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
3497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
3597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
3697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
3797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
3897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
3997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardComposer;
4097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.android.vcard.VCardConfig;
4197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Lists;
4297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Maps;
4397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport com.google.android.collect.Sets;
4497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
45b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
46caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikovimport android.accounts.AccountManager;
475b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanaimport android.accounts.OnAccountsUpdateListener;
48bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.Notification;
49bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.NotificationManager;
50bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.app.PendingIntent;
51c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.app.SearchManager;
52568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderOperation;
53568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.ContentProviderResult;
546ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.content.ContentResolver;
5535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
5667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.ContentValues;
5767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.Context;
58627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.IContentService;
59bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.content.Intent;
60568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikovimport android.content.OperationApplicationException;
613d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.content.SharedPreferences;
62627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikovimport android.content.SyncAdapterType;
6367dde51ab932dc84d95a203b113989b13437f13dJeff Sharkeyimport android.content.UriMatcher;
641129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikovimport android.database.CharArrayBuffer;
654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
66ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.CursorWrapper;
67ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
6809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor;
6909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.database.MatrixCursor.RowBuilder;
70a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikovimport android.database.sqlite.SQLiteConstraintException;
714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
73c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
75d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.net.Uri.Builder;
7651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikovimport android.os.AsyncTask;
776ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshiimport android.os.Bundle;
78ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringertimport android.os.ParcelFileDescriptor;
79b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
800e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport android.os.SystemProperties;
813d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikovimport android.preference.PreferenceManager;
82508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
833de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract;
84b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
8597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
8697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
8797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
8897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
8997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname;
9097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
9197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
9297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo;
9397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
9497fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
95ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
963de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
975b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions;
983de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Data;
99d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
1005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.DisplayNameSources;
1015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.FullNameStyle;
1023de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Groups;
103bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
1043de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
1055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.PhoneticNameStyle;
10609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
1073de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
108916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
1093de6754a90e3682f2f52b99621d0fded060b99aeDmitri Plotnikovimport android.provider.ContactsContract.Settings;
11082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.StatusUpdates;
11197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.LiveFolders;
11297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.OpenableColumns;
11397fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikovimport android.provider.SyncStateContract;
114a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
115a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
116c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.util.Log;
1174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
118d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.ByteArrayOutputStream;
119b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikovimport java.io.FileNotFoundException;
120d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.IOException;
121d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkeyimport java.io.OutputStream;
12242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.text.SimpleDateFormat;
1237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
1245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.Collections;
12542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmannimport java.util.Date;
126b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
1270e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.HashSet;
1285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikovimport java.util.List;
129622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkeyimport java.util.Locale;
130b5a4add17815167d20a90645779df34cdf45280dFred Quintanaimport java.util.Map;
1310e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriffimport java.util.Set;
132ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikovimport java.util.concurrent.CountDownLatch;
1334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
1354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
1364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
1374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
1385b4b305b9698526c6e82e0e01448b0a5642dd505Fred Quintanapublic class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
139caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
140bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final String TAG = "ContactsProvider";
141bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov
142bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
1434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
144619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
145619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
146619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
1483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
1493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1503d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /**
151b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov     * Property key for the legacy contact import version. The need for a version
1523d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * as opposed to a boolean flag is that if we discover bugs in the contact import process,
1533d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     * we can trigger re-import by incrementing the import version.
1543d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov     */
155b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final String PROPERTY_CONTACTS_IMPORTED = "contacts_imported_v1";
156b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov    private static final int PROPERTY_CONTACTS_IMPORT_VERSION = 1;
15751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    private static final String PREF_LOCALE = "locale";
1583d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
1590e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff    private static final String AGGREGATE_CONTACTS = "sync.contacts.aggregate";
1600e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff
161a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
1624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
163dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov    private static final String TIMES_CONTACTED_SORT_COLUMN = "times_contacted_sort";
1645e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
165d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final String STREQUENT_ORDER_BY = Contacts.STARRED + " DESC, "
166dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov            + TIMES_CONTACTED_SORT_COLUMN + " DESC, "
1679b43551f1ce33b79141772737a262ce609bd0cebMegha Joshi            + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
168d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
169d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            "(SELECT COUNT(1) FROM " + Tables.CONTACTS + " WHERE "
170d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            + Contacts.STARRED + "=1) + 25";
171d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1726e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_CONTACTS_TABLE =
1739b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.CONTACTS + " SET " + Contacts.TIMES_CONTACTED + "=" +
1749b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + Contacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1759b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + Contacts.TIMES_CONTACTED + " + 1) END WHERE " + Contacts._ID + "=?";
1769b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
1776e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori    /* package */ static final String UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE =
1789b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            "UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.TIMES_CONTACTED + "=" +
1799b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " CASE WHEN " + RawContacts.TIMES_CONTACTED + " IS NULL THEN 1 ELSE " +
1809b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            " (" + RawContacts.TIMES_CONTACTED + " + 1) END WHERE " + RawContacts.CONTACT_ID + "=?";
1819b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori
182de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa    /* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
183de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa
184d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS = 1000;
185d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private static final int CONTACTS_ID = 1001;
1865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP = 1002;
1875870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID = 1003;
188a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_DATA = 1004;
1895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_FILTER = 1005;
1905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT = 1006;
1915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_STREQUENT_FILTER = 1007;
1925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private static final int CONTACTS_GROUP = 1008;
193a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_PHOTO = 1009;
194f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    private static final int CONTACTS_AS_VCARD = 1010;
19542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann    private static final int CONTACTS_AS_MULTI_VCARD = 1011;
1962149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_DATA = 1012;
1972149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_DATA = 1013;
198a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_ID_ENTITIES = 1014;
199a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ENTITIES = 1015;
200a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private static final int CONTACTS_LOOKUP_ID_ENTITIES = 1016;
2014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS = 2002;
2035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_ID = 2003;
2045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    private static final int RAW_CONTACTS_DATA = 2004;
20546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITY_ID = 2005;
2064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
2086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
209ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
21048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_ID = 3003;
21148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int PHONES_FILTER = 3004;
21248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS = 3005;
21348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_ID = 3006;
21448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_LOOKUP = 3007;
21548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int EMAILS_FILTER = 3008;
21648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS = 3009;
21748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov    private static final int POSTALS_ID = 3010;
218a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
2206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
221b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
222b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
223b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
22482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES = 7000;
22582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private static final int STATUS_UPDATES_ID = 7001;
2261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
22731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
22831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
229eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    private static final int SETTINGS = 9000;
230eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
231ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
232ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
233ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
234ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
23535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
236b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private static final int SYNCSTATE_ID = 11001;
23735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
238c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SUGGESTIONS = 12001;
239c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    private static final int SEARCH_SHORTCUT = 12002;
240c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
2411b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS = 14000;
2421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_WITH_PHONES = 14001;
2431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_FAVORITES = 14002;
2441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    private static final int LIVE_FOLDERS_CONTACTS_GROUP_NAME = 14003;
2451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
24646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    private static final int RAW_CONTACT_ENTITIES = 15001;
24746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
24809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private static final int PROVIDER_STATUS = 16001;
24909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
250d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES = 17001;
251d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final int DIRECTORIES_ID = 17002;
252d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
2537a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private static final int COMPLETE_NAME = 18000;
2547a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
255dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
256dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
257dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME
258dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
259dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE
260dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + "=" + RawContactsColumns.CONCRETE_ACCOUNT_TYPE
261dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " AND " + Groups.FAVORITES + " != 0";
262dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
263dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID =
264dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            RawContactsColumns.CONCRETE_ID + "=? AND "
265dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
266dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AND "
267dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
268dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AND "
269dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + Groups.AUTO_ADD + " != 0";
270dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
271dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String[] PROJECTION_GROUP_ID
272dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            = new String[]{Tables.GROUPS + "." + Groups._ID};
273dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
274dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_GROUPMEMBERSHIP_DATA = DataColumns.MIMETYPE_ID + "=? "
275dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.GROUP_ROW_ID + "=? "
276dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            + "AND " + GroupMembership.RAW_CONTACT_ID + "=?";
277dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
278dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private static final String SELECTION_STARRED_FROM_RAW_CONTACTS =
279dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            "SELECT " + RawContacts.STARRED
280dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?";
281dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
282d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private interface DataContactsQuery {
283f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        public static final String TABLE = "data "
284f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
285f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";
28667dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
28767dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String[] PROJECTION = new String[] {
2886cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov            RawContactsColumns.CONCRETE_ID,
2893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
290f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov            ContactsColumns.CONCRETE_ID
291ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
292ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
293d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 0;
29467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final int DATA_ID = 1;
295d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        public static final int CONTACT_ID = 2;
296ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
2971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
29814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataDeleteQuery {
29967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
3003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
30188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] CONCRETE_COLUMNS = new String[] {
3023cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            DataColumns.CONCRETE_ID,
3033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            MimetypesColumns.MIMETYPE,
3045ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            Data.RAW_CONTACT_ID,
3053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            Data.IS_PRIMARY,
306f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
30788e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        };
30888e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov
30988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
31088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data._ID,
31188e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            MimetypesColumns.MIMETYPE,
31288e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.RAW_CONTACT_ID,
31388e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov            Data.IS_PRIMARY,
314f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            Data.DATA1,
3153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        };
3163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
31714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public static final int _ID = 0;
3183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int MIMETYPE = 1;
3195ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public static final int RAW_CONTACT_ID = 2;
3203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public static final int IS_PRIMARY = 3;
321f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public static final int DATA1 = 4;
3223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
3233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
32414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    private interface DataUpdateQuery {
325321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
32620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
32720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int _ID = 0;
328321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int RAW_CONTACT_ID = 1;
329321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        int MIMETYPE = 2;
33020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
33120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
332f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
33319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    private interface RawContactsQuery {
33419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String TABLE = Tables.RAW_CONTACTS;
33519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
33619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        String[] COLUMNS = new String[] {
337ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.DELETED,
338ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
339ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
34019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        };
34119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
34219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int DELETED = 0;
343ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_TYPE = 1;
344ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int ACCOUNT_NAME = 2;
34519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka    }
34619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka
347c76cdd0723b99f478c9ba5329d14a971cd8dfb3dCostin Manolache    public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
348df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana    public static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE = "legacy_hosted_or_google";
349caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
35071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    /** Sql where statement for filtering on groups. */
35171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov    private static final String CONTACTS_IN_GROUP_SELECT =
35271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov            Contacts._ID + " IN "
35371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + "(SELECT " + RawContacts.CONTACT_ID
35471e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " FROM " + Tables.RAW_CONTACTS
35571e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + " IN "
35671e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
35771e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " FROM " + Tables.DATA_JOIN_MIMETYPES
35871e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                            + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
35971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "' AND " + GroupMembership.GROUP_ROW_ID + "="
36071e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + "(SELECT " + Tables.GROUPS + "." + Groups._ID
36171e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " FROM " + Tables.GROUPS
36271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                                    + " WHERE " + Groups.TITLE + "=?)))";
36371e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov
364a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating DIRTY flag on multiple raw contacts */
365a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_DIRTY_SQL =
366a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
367a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.DIRTY + "=1" +
368a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
369a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
370a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /** Sql for updating VERSION on multiple raw contacts */
371a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private static final String UPDATE_RAW_CONTACT_SET_VERSION_SQL =
372a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            "UPDATE " + Tables.RAW_CONTACTS +
373a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " SET " + RawContacts.VERSION + " = " + RawContacts.VERSION + " + 1" +
374a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            " WHERE " + RawContacts._ID + " IN (";
375a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
376916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Name lookup types used for contact filtering */
377916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private static final String CONTACT_LOOKUP_NAME_TYPES =
378916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_COLLATION_KEY + "," +
379916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.EMAIL_BASED_NICKNAME + "," +
380916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NICKNAME + "," +
381916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            NameLookupType.NAME_SHORTHAND + "," +
382f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.ORGANIZATION + "," +
383f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            NameLookupType.NAME_CONSONANTS;
384916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
385916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
386f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsColumns = ProjectionMap.builder()
387f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CUSTOM_RINGTONE)
388f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME)
389f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_ALTERNATIVE)
390f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.DISPLAY_NAME_SOURCE)
391f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.IN_VISIBLE_GROUP)
392f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LAST_TIME_CONTACTED)
393f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.LOOKUP_KEY)
394f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME)
395f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHONETIC_NAME_STYLE)
396f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.PHOTO_ID)
397f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SEND_TO_VOICEMAIL)
398f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_ALTERNATIVE)
399f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.SORT_KEY_PRIMARY)
400f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.STARRED)
401f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.TIMES_CONTACTED)
402f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
403f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
404f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsPresenceColumns = ProjectionMap.builder()
405f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
406f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.PRESENCE)
407f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
408f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
409f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
410f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
411f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
412f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
413f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
414f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
415f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
416f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
417f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
418f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
419f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
420f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
421f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSnippetColumns = ProjectionMap.builder()
422f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_MIMETYPE)
423f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA_ID)
424f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA1)
425f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA2)
426f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA3)
427f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(SearchSnippetColumns.SNIPPET_DATA4)
428f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
429f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
430f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
431f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactColumns = ProjectionMap.builder()
432f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_NAME)
433f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.ACCOUNT_TYPE)
434f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DIRTY)
435f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.NAME_VERIFIED)
436f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SOURCE_ID)
437f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.VERSION)
438f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
439f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
440f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactSyncColumns = ProjectionMap.builder()
441f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC1)
442f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC2)
443f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC3)
444f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SYNC4)
445f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
446f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
447f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataColumns = ProjectionMap.builder()
448f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA1)
449f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA2)
450f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA3)
451f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA4)
452f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA5)
453f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA6)
454f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA7)
455f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA8)
456f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA9)
457f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA10)
458f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA11)
459f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA12)
460f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA13)
461f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA14)
462f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA15)
463f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.DATA_VERSION)
464f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_PRIMARY)
465f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.IS_SUPER_PRIMARY)
466f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.MIMETYPE)
467f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RES_PACKAGE)
468f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC1)
469f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC2)
470f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC3)
471f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.SYNC4)
472f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(GroupMembership.GROUP_SOURCE_ID)
473f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
474f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
475f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactPresenceColumns = ProjectionMap.builder()
476f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_PRESENCE,
477f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.PRESENCE)
478f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_CHAT_CAPABILITY,
479f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    Tables.AGGREGATED_PRESENCE + '.' + StatusUpdates.CHAT_CAPABILITY)
480f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS,
481f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS)
482f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_TIMESTAMP,
483f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
484f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_RES_PACKAGE,
485f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
486f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_LABEL,
487f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_LABEL)
488f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.CONTACT_STATUS_ICON,
489f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    ContactsStatusUpdatesColumns.CONCRETE_STATUS_ICON)
490f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
491f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
492f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataPresenceColumns = ProjectionMap.builder()
493f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.PRESENCE, Tables.PRESENCE + "." + StatusUpdates.PRESENCE)
494f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CHAT_CAPABILITY, Tables.PRESENCE + "." + StatusUpdates.CHAT_CAPABILITY)
495f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS, StatusUpdatesColumns.CONCRETE_STATUS)
496f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_TIMESTAMP, StatusUpdatesColumns.CONCRETE_STATUS_TIMESTAMP)
497f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_RES_PACKAGE, StatusUpdatesColumns.CONCRETE_STATUS_RES_PACKAGE)
498f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_LABEL, StatusUpdatesColumns.CONCRETE_STATUS_LABEL)
499f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
500f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
501f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
502038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana    /** Contains just BaseColumns._COUNT */
503f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
504f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(BaseColumns._COUNT, "COUNT(*)")
505f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
506f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
507e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov    /** Contains just the contacts columns */
508f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionMap = ProjectionMap.builder()
509f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts._ID)
510f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.HAS_PHONE_NUMBER)
511f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.NAME_RAW_CONTACT_ID)
512f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
513f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsPresenceColumns)
514f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
515f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
516916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /** Contains just the contacts columns */
517f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsProjectionWithSnippetMap = ProjectionMap.builder()
518f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
519f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sSnippetColumns)
520f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
521916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5225e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    /** Used for pushing starred contacts to the top of a times contacted list **/
523f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentStarredProjectionMap = ProjectionMap.builder()
524f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
525f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, String.valueOf(Long.MAX_VALUE))
526f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
527f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
528f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStrequentFrequentProjectionMap = ProjectionMap.builder()
529f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsProjectionMap)
530f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(TIMES_CONTACTED_SORT_COLUMN, Contacts.TIMES_CONTACTED)
531f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
532f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
533f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey    /** Contains just the contacts vCard columns */
534f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sContactsVCardProjectionMap = ProjectionMap.builder()
535f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.DISPLAY_NAME, Contacts.DISPLAY_NAME + " || '.vcf'")
536f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(OpenableColumns.SIZE, "NULL")
537f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
538f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
539ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    /** Contains just the raw contacts columns */
540f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawContactsProjectionMap = ProjectionMap.builder()
541f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
542f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
543f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
544f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_PRIMARY)
545f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_ALTERNATIVE)
546f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DISPLAY_NAME_SOURCE)
547f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME)
548f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.PHONETIC_NAME_STYLE)
549f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_PRIMARY)
550f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SORT_KEY_ALTERNATIVE)
551f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.TIMES_CONTACTED)
552f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.LAST_TIME_CONTACTED)
553f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CUSTOM_RINGTONE)
554f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.SEND_TO_VOICEMAIL)
555f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
556f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.AGGREGATION_MODE)
557f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
558f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
559f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
560f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
561a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the raw entity view*/
562f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sRawEntityProjectionMap = ProjectionMap.builder()
563f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts._ID)
564f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
565f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.Entity.DATA_ID)
566f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.IS_RESTRICTED)
567f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.DELETED)
568f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.STARRED)
569f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
570f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
571f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
572f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
573f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
574a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /** Contains the columns from the contact entity view*/
575f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sEntityProjectionMap = ProjectionMap.builder()
576f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity._ID)
577f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.CONTACT_ID)
578f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.RAW_CONTACT_ID)
579f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DATA_ID)
580f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.NAME_RAW_CONTACT_ID)
581f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.DELETED)
582f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Contacts.Entity.IS_RESTRICTED)
583f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
584f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
585f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
586f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactSyncColumns)
587f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
588f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
589f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
590f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
5914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /** Contains columns from the data view */
592f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDataProjectionMap = ProjectionMap.builder()
593f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID)
594f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.RAW_CONTACT_ID)
595f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.CONTACT_ID)
596f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data.NAME_RAW_CONTACT_ID)
597f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
598f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
599f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sRawContactColumns)
600f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
601f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
602f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
603f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    /** Contains columns from the data view */
605f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDistinctDataProjectionMap = ProjectionMap.builder()
606f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Data._ID, "MIN(" + Data._ID + ")")
607f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(RawContacts.CONTACT_ID)
608f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataColumns)
609f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sDataPresenceColumns)
610f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactsColumns)
611f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sContactPresenceColumns)
612f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
613f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
6149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
615f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sPhoneLookupProjectionMap = ProjectionMap.builder()
616f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup._ID, "contacts_view." + Contacts._ID)
617f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LOOKUP_KEY, "contacts_view." + Contacts.LOOKUP_KEY)
618f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.DISPLAY_NAME, "contacts_view." + Contacts.DISPLAY_NAME)
619f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LAST_TIME_CONTACTED, "contacts_view." + Contacts.LAST_TIME_CONTACTED)
620f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TIMES_CONTACTED, "contacts_view." + Contacts.TIMES_CONTACTED)
621f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.STARRED, "contacts_view." + Contacts.STARRED)
622f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.IN_VISIBLE_GROUP, "contacts_view." + Contacts.IN_VISIBLE_GROUP)
623f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.PHOTO_ID, "contacts_view." + Contacts.PHOTO_ID)
624f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.CUSTOM_RINGTONE, "contacts_view." + Contacts.CUSTOM_RINGTONE)
625f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.HAS_PHONE_NUMBER, "contacts_view." + Contacts.HAS_PHONE_NUMBER)
626f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.SEND_TO_VOICEMAIL, "contacts_view." + Contacts.SEND_TO_VOICEMAIL)
627f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.NUMBER, Phone.NUMBER)
628f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.TYPE, Phone.TYPE)
629f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PhoneLookup.LABEL, Phone.LABEL)
6302530512f639c4979fd7371c7dd25dd67e8118124Bai Tao            .add(PhoneLookup.NORMALIZED_NUMBER, Phone.NORMALIZED_NUMBER)
631f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
632f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
633ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
634f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsProjectionMap = ProjectionMap.builder()
635f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups._ID)
636f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_NAME)
637f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.ACCOUNT_TYPE)
638f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SOURCE_ID)
639f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DIRTY)
640f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.VERSION)
641f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.RES_PACKAGE)
642f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE)
643f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.TITLE_RES)
644f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.GROUP_VISIBLE)
645f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYSTEM_ID)
646f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.DELETED)
647f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.NOTES)
648f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SHOULD_SYNC)
649f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.FAVORITES)
650f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.AUTO_ADD)
651f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC1)
652f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC2)
653f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC3)
654f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SYNC4)
655f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
656f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
657ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
658f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sGroupsSummaryProjectionMap = ProjectionMap.builder()
659f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .addAll(sGroupsProjectionMap)
660f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_COUNT,
661f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
662f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
663f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
664f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
665f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ")")
666f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Groups.SUMMARY_WITH_PHONES,
667f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(DISTINCT " + ContactsColumns.CONCRETE_ID
668f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + ") FROM " + Tables.DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS
669f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " WHERE " + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP
670f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Clauses.BELONGS_TO_GROUP
671f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " AND " + Contacts.HAS_PHONE_NUMBER + ")")
672f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
673f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
674373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov    /** Contains the agg_exceptions columns */
675f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sAggregationExceptionsProjectionMap = ProjectionMap.builder()
676f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id")
677f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.TYPE)
678f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID1)
679f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(AggregationExceptions.RAW_CONTACT_ID2)
680f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
681f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
682eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey    /** Contains the agg_exceptions columns */
683f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sSettingsProjectionMap = ProjectionMap.builder()
684f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_NAME)
685f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ACCOUNT_TYPE)
686f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_VISIBLE)
687f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.SHOULD_SYNC)
688f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.ANY_UNSYNCED,
689f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN MIN(" + Settings.SHOULD_SYNC
690f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                        + ",(SELECT "
691f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + "(CASE WHEN MIN(" + Groups.SHOULD_SYNC + ") IS NULL"
692f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " THEN 1"
693f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " ELSE MIN(" + Groups.SHOULD_SYNC + ")"
694f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " END)"
695f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.GROUPS
696f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + GroupsColumns.CONCRETE_ACCOUNT_NAME + "="
697f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_NAME
698f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                + " AND " + GroupsColumns.CONCRETE_ACCOUNT_TYPE + "="
699f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                                    + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "))=0"
700f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN 1"
701f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE 0"
702f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " END)")
703f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_COUNT,
704f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
705f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
706f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
707f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
708f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
709f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
710f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Settings.UNGROUPED_WITH_PHONES,
711f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(SELECT COUNT(*)"
712f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " FROM (SELECT 1"
713f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " FROM " + Tables.SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS
714f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " WHERE " + Contacts.HAS_PHONE_NUMBER
715f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " GROUP BY " + Clauses.GROUP_BY_ACCOUNT_CONTACT_ID
716f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                            + " HAVING " + Clauses.HAVING_NO_GROUPS
717f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + "))")
718f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
719f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
72082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Contains StatusUpdates columns */
721f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sStatusUpdatesProjectionMap = ProjectionMap.builder()
722f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(PresenceColumns.RAW_CONTACT_ID)
723f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.DATA_ID, DataColumns.CONCRETE_ID)
724f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_ACCOUNT)
725f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.IM_HANDLE)
726f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PROTOCOL)
727f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // We cannot allow a null in the custom protocol field, because SQLite3 does not
728f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // properly enforce uniqueness of null values
729f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CUSTOM_PROTOCOL,
730f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    "(CASE WHEN " + StatusUpdates.CUSTOM_PROTOCOL + "=''"
731f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " THEN NULL"
732f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                    + " ELSE " + StatusUpdates.CUSTOM_PROTOCOL + " END)")
733f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.PRESENCE)
734f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.CHAT_CAPABILITY)
735f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS)
736f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_TIMESTAMP)
737f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_RES_PACKAGE)
738f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_ICON)
739f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(StatusUpdates.STATUS_LABEL)
740f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
741f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
7421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    /** Contains Live Folders columns */
743f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sLiveFoldersProjectionMap = ProjectionMap.builder()
744f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders._ID, Contacts._ID)
745f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(LiveFolders.NAME, Contacts.DISPLAY_NAME)
746f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // TODO: Put contact photo back when we have a way to display a default icon
747f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // for contacts without a photo
748f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            // .add(LiveFolders.ICON_BITMAP, Photos.DATA)
749f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
750f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov
751d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /** Contains {@link Directory} columns */
752f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov    private static final ProjectionMap sDirectoryProjectionMap = ProjectionMap.builder()
753f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory._ID)
754f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.PACKAGE_NAME)
755f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.TYPE_RESOURCE_ID)
756f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DISPLAY_NAME)
757f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.DIRECTORY_AUTHORITY)
758f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_TYPE)
759f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.ACCOUNT_NAME)
760f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .add(Directory.EXPORT_SUPPORT)
761f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            .build();
7627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
7639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    // where clause to update the status_updates table
7649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE =
7659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdatesColumns.DATA_ID + " IN (SELECT Distinct " + StatusUpdates.DATA_ID +
7669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " FROM " + Tables.STATUS_UPDATES + " LEFT OUTER JOIN " + Tables.PRESENCE +
7679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            " ON " + StatusUpdatesColumns.DATA_ID + " = " + StatusUpdates.DATA_ID + " WHERE ";
7689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
7692526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private static final String[] EMPTY_STRING_ARRAY = new String[0];
7702526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
771bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
772bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Notification ID for failure to import contacts.
773bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
774bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private static final int LEGACY_IMPORT_FAILED_NOTIFICATION = 1;
77551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
776c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
777c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
7783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for setting a data record to the super primary. */
779c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
7803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /** Precompiled sql statement for updating a contact display name */
78125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private SQLiteStatement mRawContactDisplayNameUpdate;
78282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    /** Precompiled sql statement for updating an aggregated status update */
783a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mLastStatusUpdate;
784f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
785f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private SQLiteStatement mNameLookupDelete;
786a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateAutoTimestamp;
787a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateInsert;
788a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateReplace;
7890a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private SQLiteStatement mStatusAttributionUpdate;
790a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov    private SQLiteStatement mStatusUpdateDelete;
791f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov    private SQLiteStatement mResetNameVerifiedForOtherRawContacts;
792a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
793f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdEmail;
794f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private long mMimeTypeIdIm;
7951129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdStructuredName;
7961129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdOrganization;
7971129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdNickname;
7981129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private long mMimeTypeIdPhone;
799f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov    private StringBuilder mSb = new StringBuilder();
8001129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs1 = new String[1];
8011129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private String[] mSelectionArgs2 = new String[2];
8022526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov    private ArrayList<String> mSelectionArgs = Lists.newArrayList();
8032526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov
804f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private Account mAccount;
805f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
8064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
8074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
808a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
809d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
810d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
811a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
812a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
8133653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
8143653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
8152d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
8162d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                AGGREGATION_SUGGESTIONS);
817a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
818c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
8195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
8205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
8212149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
8225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
8232149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
8242149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                CONTACTS_LOOKUP_ID_DATA);
825a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
826a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ENTITIES);
827a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
828a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                CONTACTS_LOOKUP_ID_ENTITIES);
829f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
83042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann        matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
83142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                CONTACTS_AS_MULTI_VCARD);
8325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
833ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
834ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                CONTACTS_STREQUENT_FILTER);
8355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
8363653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
8375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
8385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
8395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA);
84046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID);
84146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
84246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
843b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
8444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
8454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
846ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
84748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
8485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
849ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
8504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
85148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
8525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
8535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
8544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
855ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
85648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
8571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
858ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
859ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
860ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
861ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
86235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
863b5a4add17815167d20a90645779df34cdf45280dFred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
864b5a4add17815167d20a90645779df34cdf45280dFred Quintana                SYNCSTATE_ID);
86535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
866a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
867b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
868b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
869b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
870b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
8714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
872eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
873eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
87482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
87582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
8761f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
877c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
878c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
879c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
880c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SUGGESTIONS);
8812d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill        matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
882c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                SEARCH_SHORTCUT);
883c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
8841b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts",
8851b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS);
8861b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*",
8871b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_GROUP_NAME);
8881b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones",
8891b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_WITH_PHONES);
8901b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites",
8911b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                LIVE_FOLDERS_CONTACTS_FAVORITES);
89209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
89309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
894d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
895d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
896d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
8977a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
8987a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
89919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
90019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
901d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static class DirectoryInfo {
902d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String authority;
903d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountName;
904d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String accountType;
905d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
906d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
907d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
908d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Cached information about contact directories.
909d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
9104458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private HashMap<String, DirectoryInfo> mDirectoryCache = new HashMap<String, DirectoryInfo>();
9114458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov    private boolean mDirectoryCacheValid = false;
912d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
9133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    /**
9143cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     * Handles inserts and update for a specific Data type.
9153cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov     */
9163cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private abstract class DataRowHandler {
9173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9183cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected final String mMimetype;
919653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long mMimetypeId;
9203cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9211129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        @SuppressWarnings("all")
9223cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public DataRowHandler(String mimetype) {
9233cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mMimetype = mimetype;
924a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
925a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            // To ensure the data column position. This is dead code if properly configured.
926a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
927a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
928a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    || Email.DATA != Data.DATA1) {
929a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
930a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                        + " data is not in DATA1 column");
931a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            }
9323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
934653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        protected long getMimeTypeId() {
935653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (mMimetypeId == 0) {
936b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
937653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
938653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return mMimetypeId;
939653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
940653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
9413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
9423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Inserts a row into the {@link Data} table.
9433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
9445ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
945e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            final long dataId = db.insert(Tables.DATA, null, values);
946e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
947e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            Integer primary = values.getAsInteger(Data.IS_PRIMARY);
948e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (primary != null && primary != 0) {
949653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
950e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
951e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
952e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return dataId;
9533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9543cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9553cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
9563cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * Validates data and updates a {@link Data} row using the cursor, which contains
9573cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         * the current data.
958813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         *
959813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * @return true if update changed something
9603cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
961813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
962f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
96314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
96414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
965653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
966653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.containsKey(Data.IS_SUPER_PRIMARY)) {
967653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                long mimeTypeId = getMimeTypeId();
968653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
969653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, mimeTypeId);
970653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
971653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting these, remove them from "values".
972653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_SUPER_PRIMARY);
973653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
974653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            } else if (values.containsKey(Data.IS_PRIMARY)) {
975653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setIsPrimary(rawContactId, dataId, getMimeTypeId());
976653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
977653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                // Now that we've taken care of setting this, remove it from "values".
978653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(Data.IS_PRIMARY);
979653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
980653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
981653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (values.size() > 0) {
9824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
9834da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mDb.update(Tables.DATA, values, Data._ID + " =?", mSelectionArgs1);
984653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
985653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
986f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (!callerIsSyncAdapter) {
987653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                setRawContactDirty(rawContactId);
988653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
989813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
990813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
9913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
9923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
9933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
99414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
99514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
99614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
9974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
9984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            int count = db.delete(Tables.DATA, Data._ID + "=?", mSelectionArgs1);
9994da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
10004da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
10013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            if (count != 0 && primary) {
10025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                fixPrimary(db, rawContactId);
10033cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            return count;
10053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10075ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        private void fixPrimary(SQLiteDatabase db, long rawContactId) {
10084da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            long mimeTypeId = getMimeTypeId();
1009e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            long primaryId = -1;
1010e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            int primaryType = -1;
10114da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
10124da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            Cursor c = db.query(DataDeleteQuery.TABLE,
10134da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    DataDeleteQuery.CONCRETE_COLUMNS,
10144da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    Data.RAW_CONTACT_ID + "=?" +
10154da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        " AND " + DataColumns.MIMETYPE_ID + "=" + mimeTypeId,
10164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
10173cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            try {
1018e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                while (c.moveToNext()) {
101914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                    long dataId = c.getLong(DataDeleteQuery._ID);
1020f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    int type = c.getInt(DataDeleteQuery.DATA1);
1021e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
1022e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryId = dataId;
1023e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                        primaryType = type;
1024e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                    }
10253cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                }
10263cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            } finally {
10273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                c.close();
10283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
10294da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            if (primaryId != -1) {
10304da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                setIsPrimary(rawContactId, primaryId, mimeTypeId);
10314da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            }
1032e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1033e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1034e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        /**
1035e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * Returns the rank of a specific record type to be used in determining the primary
1036e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         * row. Lower number represents higher priority.
1037e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov         */
1038e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1039e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            return 0;
10403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
104225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        protected void fixRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
1043285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1044d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                updateRawContactDisplayName(db, rawContactId);
1045fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(db, rawContactId);
1046285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
10473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
1048a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1049622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1050622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Return set of values, using current values at given {@link Data#_ID}
1051813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * as baseline, but augmented with any updates.  Returns null if there is
1052813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov         * no change.
1053622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1054622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
1055622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                ContentValues update) {
1056813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            boolean changing = false;
1057622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues values = new ContentValues();
10584da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
10594da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=?",
10604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1, null, null, null);
1061622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            try {
1062622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                if (cursor.moveToFirst()) {
1063622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    for (int i = 0; i < cursor.getColumnCount(); i++) {
1064622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        final String key = cursor.getColumnName(i);
1065813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        final String value = cursor.getString(i);
1066813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        if (!changing && update.containsKey(key)) {
1067813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            Object newValue = update.get(key);
1068813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            String newString = newValue == null ? null : newValue.toString();
1069813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                            changing |= !TextUtils.equals(newString, value);
1070813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        }
1071813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                        values.put(key, value);
1072622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                    }
1073622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                }
1074622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            } finally {
1075622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                cursor.close();
1076622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1077813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!changing) {
1078813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return null;
1079813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1080813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1081622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            values.putAll(update);
1082622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return values;
1083622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
10843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CustomDataRowHandler extends DataRowHandler {
10873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CustomDataRowHandler(String mimetype) {
10893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
10903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
10913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
10923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
10933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class StructuredNameRowHandler extends DataRowHandler {
1094622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final NameSplitter mSplitter;
10953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1096622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredNameRowHandler(NameSplitter splitter) {
10973cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(StructuredName.CONTENT_ITEM_TYPE);
1098622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
10993cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11013cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
11025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1103622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(values, values);
110414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
110514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
110614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1107f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String name = values.getAsString(StructuredName.DISPLAY_NAME);
1108d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            Integer fullNameStyle = values.getAsInteger(StructuredName.FULL_NAME_STYLE);
1109d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            insertNameLookupForStructuredName(rawContactId, dataId, name,
111051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                    fullNameStyle != null
111151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            ? mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)
111251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                            : FullNameStyle.UNDEFINED);
111348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId, values);
111425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1115813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
111614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
111714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
111814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
111914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1120813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1121f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1122622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1123622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1124cabac02a2416b495e030654accffcbb5ae526678Dmitri Plotnikov
1125622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1126813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {  // No change
1127813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1128813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1129813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1130622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredNameComponents(augmented, values);
113114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1132f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
11337ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov            if (values.containsKey(StructuredName.DISPLAY_NAME) ||
11347ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_FAMILY_NAME) ||
11357ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME) ||
11367ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                    values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)) {
11377ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                augmented.putAll(values);
11387ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                String name = augmented.getAsString(StructuredName.DISPLAY_NAME);
1139f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                deleteNameLookup(dataId);
11407ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                Integer fullNameStyle = augmented.getAsInteger(StructuredName.FULL_NAME_STYLE);
1141d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                insertNameLookupForStructuredName(rawContactId, dataId, name,
114251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                        fullNameStyle != null
114351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                ? mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)
114451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                                : FullNameStyle.UNDEFINED);
11457ceafd016eb07d2de808d18cd5a9463efaee781dDmitri Plotnikov                insertNameLookupForPhoneticName(rawContactId, dataId, augmented);
114614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            }
114725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1148813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
1149813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
115014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
115114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
115214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
115314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
115414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
115514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
115614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
115714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
115814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1159f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
116025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1161813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
116214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
11633cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
11643cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
11653cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        /**
1166622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
11673cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov         */
1168622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1169622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
1170622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredName.FAMILY_NAME, StructuredName.SUFFIX
1171622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
11723cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1173622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1174622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Parses the supplied display name, but only if the incoming values do
1175622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * not already contain structured name parts. Also, if the display name
1176622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * is not provided, generate one by concatenating first name and last
1177622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * name.
1178622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
11797a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        public void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
118067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
1181622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
118267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
118367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1184622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1185622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
11868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1187622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(name, unstruct);
1188622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.toValues(update);
118967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
119067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
119167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // We need to update the display name when any structured components
119267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // are specified, even when they are null, which is why we are checking
119367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // areAnySpecified.  The touchedStruct in the condition is an optimization:
119467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // if there are non-null values, we know for a fact that some values are present.
11958c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                NameSplitter.Name name = new NameSplitter.Name();
1196622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                name.fromValues(augmented);
11974cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                // As the name could be changed, let's guess the name style again.
11984cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                name.fullNameStyle = FullNameStyle.UNDEFINED;
11994cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                mSplitter.guessNameStyle(name);
1200ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                int unadjustedFullNameStyle = name.fullNameStyle;
1201ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                name.fullNameStyle = mSplitter.getAdjustedFullNameStyle(name.fullNameStyle);
12025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                final String joined = mSplitter.join(name, true);
1203622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredName.DISPLAY_NAME, joined);
12045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
1205ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                update.put(StructuredName.FULL_NAME_STYLE, unadjustedFullNameStyle);
12065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
12074cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao            } else if (touchedUnstruct && touchedStruct){
1208d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.FULL_NAME_STYLE)) {
1209d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.FULL_NAME_STYLE,
1210d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessFullNameStyle(unstruct));
12114cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1212d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                if (!update.containsKey(StructuredName.PHONETIC_NAME_STYLE)) {
1213d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                    update.put(StructuredName.PHONETIC_NAME_STYLE,
1214d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov                            mSplitter.guessPhoneticNameStyle(unstruct));
12154cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                }
1216622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
1217622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1218622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
1219622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1220622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    public class StructuredPostalRowHandler extends DataRowHandler {
1221622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private PostalSplitter mSplitter;
1222622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1223622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public StructuredPostalRowHandler(PostalSplitter splitter) {
1224622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            super(StructuredPostal.CONTENT_ITEM_TYPE);
1225622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            mSplitter = splitter;
1226622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1227622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1228622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1229622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1230622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(values, values);
1231622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1232622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1233622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1234622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1235813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1236f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1237622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1238622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1239813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {    // No change
1240813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1241813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1242813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1243622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            fixStructuredPostalComponents(augmented, values);
1244f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            super.update(db, values, c, callerIsSyncAdapter);
1245813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1246622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
1247622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1248622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1249622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Specific list of structured fields.
1250622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1251622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private final String[] STRUCTURED_FIELDS = new String[] {
1252622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
1253622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
1254622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                StructuredPostal.COUNTRY,
1255622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        };
1256622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1257622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1258622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * Prepares the given {@link StructuredPostal} row, building
1259622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link StructuredPostal#FORMATTED_ADDRESS} to match the structured
1260622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * values when missing. When structured components are missing, the
1261622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * unstructured value is assigned to {@link StructuredPostal#STREET}.
1262622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1263622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void fixStructuredPostalComponents(ContentValues augmented, ContentValues update) {
126467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final String unstruct = update.getAsString(StructuredPostal.FORMATTED_ADDRESS);
126567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
126667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
126767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
1268622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1269622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final PostalSplitter.Postal postal = new PostalSplitter.Postal();
1270622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1271622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (touchedUnstruct && !touchedStruct) {
1272622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                mSplitter.split(postal, unstruct);
1273622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.toValues(update);
127467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            } else if (!touchedUnstruct
127567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                    && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
127667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                // See comment in
1277622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                postal.fromValues(augmented);
1278622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                final String joined = mSplitter.join(postal);
1279622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                update.put(StructuredPostal.FORMATTED_ADDRESS, joined);
12803cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
12813cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12823cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
12833cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12843cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class CommonDataRowHandler extends DataRowHandler {
12853cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12863cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mTypeColumn;
12873cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        private final String mLabelColumn;
12883cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12893cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public CommonDataRowHandler(String mimetype, String typeColumn, String labelColumn) {
12903cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(mimetype);
12913cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mTypeColumn = typeColumn;
12923cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mLabelColumn = labelColumn;
12933cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
12943cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
12953cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
12965ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1297622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(values, values);
1298622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            return super.insert(db, rawContactId, values);
1299622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
13003cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1301622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        @Override
1302813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1303f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1304622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final long dataId = c.getLong(DataUpdateQuery._ID);
1305622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final ContentValues augmented = getAugmentedValues(db, dataId, values);
1306813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (augmented == null) {        // No change
1307813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1308813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1309622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            enforceTypeAndLabel(augmented, values);
1310813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return super.update(db, values, c, callerIsSyncAdapter);
1311622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
13123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1313622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        /**
1314622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * If the given {@link ContentValues} defines {@link #mTypeColumn},
1315622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * enforce that {@link #mLabelColumn} only appears when type is
1316622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         * {@link BaseTypes#TYPE_CUSTOM}. Exception is thrown otherwise.
1317622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey         */
1318622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        private void enforceTypeAndLabel(ContentValues augmented, ContentValues update) {
1319622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasType = !TextUtils.isEmpty(augmented.getAsString(mTypeColumn));
1320622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            final boolean hasLabel = !TextUtils.isEmpty(augmented.getAsString(mLabelColumn));
13213cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1322622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            if (hasLabel && !hasType) {
1323622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                // When label exists, assert that some type is defined
1324622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                throw new IllegalArgumentException(mTypeColumn + " must be specified when "
1325622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey                        + mLabelColumn + " is defined.");
1326622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
13273cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13283cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
13293cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13303cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class OrganizationDataRowHandler extends CommonDataRowHandler {
13313cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13323cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public OrganizationDataRowHandler() {
13333cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Organization.CONTENT_ITEM_TYPE, Organization.TYPE, Organization.LABEL);
13343cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
13375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1338a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String company = values.getAsString(Organization.COMPANY);
1339a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title = values.getAsString(Organization.TITLE);
1340a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
1341a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = super.insert(db, rawContactId, values);
1342a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
134325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1344a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookupForOrganization(rawContactId, dataId, company, title);
1345a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            return dataId;
13463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
13473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
13483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
1349813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1350f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1351813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1352813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1353813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
135414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
135531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsCompany = values.containsKey(Organization.COMPANY);
135631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            boolean containsTitle = values.containsKey(Organization.TITLE);
135731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            if (containsCompany || containsTitle) {
1358813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1359813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1360813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
136131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String company;
136231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
136331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsCompany) {
136431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = values.getAsString(Organization.COMPANY);
136531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
136631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
136731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    company = DatabaseUtils.stringForQuery(db,
136831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.COMPANY +
136931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
137031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
137131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
137231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
137331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                String title;
137431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                if (containsTitle) {
137531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = values.getAsString(Organization.TITLE);
137631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                } else {
137731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(dataId);
137831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                    title = DatabaseUtils.stringForQuery(db,
137931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            "SELECT " + Organization.TITLE +
138031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " FROM " + Tables.DATA +
138131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                            " WHERE " + Data._ID + "=?", mSelectionArgs1);
138231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                }
138331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
138431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                deleteNameLookup(dataId);
138531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                insertNameLookupForOrganization(rawContactId, dataId, company, title);
138631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
138731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
138831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov            }
1389813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
139014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
139114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
139214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
139314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1394a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            long dataId = c.getLong(DataUpdateQuery._ID);
139514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
139614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
139714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
139825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1399a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            deleteNameLookup(dataId);
140014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
140114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
140214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
140314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
14043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
14053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
14063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_WORK: return 0;
14073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_CUSTOM: return 1;
14083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Organization.TYPE_OTHER: return 2;
14093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
14103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
14113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
14123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
14133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1414e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    public class EmailDataRowHandler extends CommonDataRowHandler {
1415e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1416e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        public EmailDataRowHandler() {
1417e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            super(Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL);
1418e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1419e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1420e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
14215ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1422813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String email = values.getAsString(Email.DATA);
142314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
142414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
142514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
142625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1427813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String address = insertNameLookupForEmail(rawContactId, dataId, email);
1428813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (address != null) {
1429813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1430813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
143114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
143214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
143314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
143414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1435813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1436f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1437813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1438813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1439813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
144014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1441b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Email.DATA)) {
1442813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1443813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1444813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1445b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String address = values.getAsString(Email.DATA);
1446b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                deleteNameLookup(dataId);
1447b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                insertNameLookupForEmail(rawContactId, dataId, address);
1448b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1449813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1450b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1451813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1452813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
145314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
145414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
145514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
145614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
145714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
145814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
145914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
146014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
146114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1462f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
146325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1464813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
146514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1466e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1467e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
1468e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        @Override
1469e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        protected int getTypeRank(int type) {
1470e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            switch (type) {
1471e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_HOME: return 0;
1472e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_WORK: return 1;
1473e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_CUSTOM: return 2;
1474e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                case Email.TYPE_OTHER: return 3;
1475e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov                default: return 1000;
1476e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
1477e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov        }
1478e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov    }
1479e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov
148014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    public class NicknameDataRowHandler extends CommonDataRowHandler {
148114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
148214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public NicknameDataRowHandler() {
148314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            super(Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE, Nickname.LABEL);
148414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
148514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
148614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
148714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
148814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String nickname = values.getAsString(Nickname.NAME);
148914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
149014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
149114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1492813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!TextUtils.isEmpty(nickname)) {
1493813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1494813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                insertNameLookupForNickname(rawContactId, dataId, nickname);
1495813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1496813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
149714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return dataId;
149814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
149914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
150014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
1501813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1502f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
150314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataUpdateQuery._ID);
150414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
150514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1506813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1507813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1508813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
150914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1510b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            if (values.containsKey(Nickname.NAME)) {
1511b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                String nickname = values.getAsString(Nickname.NAME);
1512b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                deleteNameLookup(dataId);
1513b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                insertNameLookupForNickname(rawContactId, dataId, nickname);
1514b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1515813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
1516b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov            }
1517813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1518813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
151914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
152014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
152114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
152214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
152314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
152414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
152514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
152614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
152714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
1528f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            deleteNameLookup(dataId);
152925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1530813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
153114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
153214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
153314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov    }
153414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
15353cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    public class PhoneDataRowHandler extends CommonDataRowHandler {
15363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        public PhoneDataRowHandler() {
15383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            super(Phone.CONTENT_ITEM_TYPE, Phone.TYPE, Phone.LABEL);
15393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
15403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
15413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
15425ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
15430b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            long dataId;
15440b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
15450b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                String number = values.getAsString(Phone.NUMBER);
15466206cd7e15b1fe63b72cc9ba32a4d84c764963ceDmitri Plotnikov
15476206cd7e15b1fe63b72cc9ba32a4d84c764963ceDmitri Plotnikov                // TODO: revert once the U_REGEX_RULE_SYNTAX problem is fixed in PhoneNumberUtils
15486206cd7e15b1fe63b72cc9ba32a4d84c764963ceDmitri Plotnikov                String numberE164 = number;
15496206cd7e15b1fe63b72cc9ba32a4d84c764963ceDmitri Plotnikov//                    PhoneNumberUtils.formatNumberToE164(number, getCurrentCountryIso());
1550892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (numberE164 != null) {
1551892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    values.put(PhoneColumns.NORMALIZED_NUMBER, numberE164);
1552892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
15530b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
1554653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1555892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, numberE164);
1556285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
155725abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1558892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (numberE164 != null) {
1559813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                    triggerAggregation(rawContactId);
1560813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                }
15610b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            } else {
15620b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov                dataId = super.insert(db, rawContactId, values);
15630b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1564653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return dataId;
1565653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1566653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1567653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1568813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1569f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1570813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String number = null;
1571813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            String normalizedNumber = null;
1572892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            String numberE164 = null;
15730b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1574813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                number = values.getAsString(Phone.NUMBER);
1575892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (number != null) {
1576892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    numberE164 =
1577892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.formatNumberToE164(number, getCurrentCountryIso());
1578892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
1579892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (numberE164 != null) {
1580892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    values.put(PhoneColumns.NORMALIZED_NUMBER, numberE164);
1581892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
1582813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1583653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1584813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1585813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1586813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1587653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1588813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (values.containsKey(Phone.NUMBER)) {
1589813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long dataId = c.getLong(DataUpdateQuery._ID);
1590813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1591892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                updatePhoneLookup(db, rawContactId, dataId, number, numberE164);
1592285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updateHasPhoneNumber(db, rawContactId);
159325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                fixRawContactDisplayName(db, rawContactId);
1594813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                triggerAggregation(rawContactId);
15950b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov            }
1596813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
159714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        }
159814bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
159914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        @Override
160014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
160114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long dataId = c.getLong(DataDeleteQuery._ID);
160214bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
160314bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
160414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            int count = super.delete(db, c);
160514bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov
160614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            updatePhoneLookup(db, rawContactId, dataId, null, null);
1607285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            mContactAggregator.updateHasPhoneNumber(db, rawContactId);
160825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov            fixRawContactDisplayName(db, rawContactId);
1609813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            triggerAggregation(rawContactId);
161014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            return count;
1611653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1612653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1613653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void updatePhoneLookup(SQLiteDatabase db, long rawContactId, long dataId,
1614892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String number, String numberE164) {
1615892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(dataId);
1616892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            db.delete(Tables.PHONE_LOOKUP, PhoneLookupColumns.DATA_ID + "=?", mSelectionArgs1);
1617e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            if (number != null) {
1618892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber = PhoneNumberUtils.normalizeNumber(number);
1619892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                if (!TextUtils.isEmpty(normalizedNumber)) {
1620892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    ContentValues phoneValues = new ContentValues();
1621892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.RAW_CONTACT_ID, rawContactId);
1622892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.DATA_ID, dataId);
1623892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, normalizedNumber);
1624892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    phoneValues.put(PhoneLookupColumns.MIN_MATCH,
1625892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                            PhoneNumberUtils.toCallerIDMinMatch(normalizedNumber));
1626892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
1627892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov
1628892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (numberE164 != null && !numberE164.equals(normalizedNumber)) {
1629892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER, numberE164);
1630892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        phoneValues.put(PhoneLookupColumns.MIN_MATCH,
1631892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                PhoneNumberUtils.toCallerIDMinMatch(numberE164));
1632892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
1633892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
1634892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                }
1635e80e514a6175ad2ee03ea6eff6201e0e47d5a710Dmitri Plotnikov            }
16363cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
16373cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
16383cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        @Override
16393cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        protected int getTypeRank(int type) {
16403cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            switch (type) {
16413cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_MOBILE: return 0;
16423cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_WORK: return 1;
16433cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_HOME: return 2;
16443cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_PAGER: return 3;
16453cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_CUSTOM: return 4;
16463cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_OTHER: return 5;
16473cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_WORK: return 6;
16483cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                case Phone.TYPE_FAX_HOME: return 7;
16493cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                default: return 1000;
16503cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            }
16513cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
16523cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
16533cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1654653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public class GroupMembershipRowHandler extends DataRowHandler {
1655653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1656dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private static final String SELECTION_RAW_CONTACT_ID = RawContacts._ID + "=?";
1657dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1658dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private static final String QUERY_COUNT_FAVORITES_GROUP_MEMBERSHIPS_BY_RAW_CONTACT_ID =
1659dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                "SELECT COUNT(*) FROM " + Tables.DATA + " LEFT OUTER JOIN " + Tables .GROUPS
1660dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " ON " + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID
1661dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + "=" + GroupsColumns.CONCRETE_ID
1662dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
1663dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " AND " + Tables.DATA + "." + GroupMembership.RAW_CONTACT_ID + "=?"
1664dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + " AND " + Groups.FAVORITES + "!=0";
1665dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1666653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public GroupMembershipRowHandler() {
1667653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            super(GroupMembership.CONTENT_ITEM_TYPE);
1668653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1669653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1670653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1671653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1672653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, true);
16730be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1674dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (hasFavoritesGroupMembership(db, rawContactId)) {
1675dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, true /* starred */);
1676dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
16770be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
16780be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return dataId;
1679653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1680653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1681653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        @Override
1682813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1683f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
168414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1685dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
1686653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            resolveGroupSourceIdInValues(rawContactId, db, values, false);
1687813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1688813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1689813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1690dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
1691dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (wasStarred != isStarred) {
1692dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, isStarred);
1693dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
16940be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
1695813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
16960be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
16970be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
1698dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private void updateRawContactsStar(SQLiteDatabase db, long rawContactId, boolean starred) {
1699dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            ContentValues rawContactValues = new ContentValues();
1700dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            rawContactValues.put(RawContacts.STARRED, starred ? 1 : 0);
1701dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (db.update(Tables.RAW_CONTACTS, rawContactValues, SELECTION_RAW_CONTACT_ID,
1702dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{Long.toString(rawContactId)}) > 0) {
1703dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mContactAggregator.updateStarred(rawContactId);
1704dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
1705dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1706dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
1707dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        private boolean hasFavoritesGroupMembership(SQLiteDatabase db, long rawContactId) {
1708dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final long groupMembershipMimetypeId = mDbHelper
1709dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
1710dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = 0 < DatabaseUtils
1711dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    .longForQuery(db, QUERY_COUNT_FAVORITES_GROUP_MEMBERSHIPS_BY_RAW_CONTACT_ID,
1712dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{Long.toString(groupMembershipMimetypeId), Long.toString(rawContactId)});
1713dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return isStarred;
1714dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
1715dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
17160be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        @Override
17170be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
17180be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1719dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
17200be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            int count = super.delete(db, c);
1721dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
1722dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (wasStarred && !isStarred) {
1723dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContactsStar(db, rawContactId, false /* starred */);
1724dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
17250be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            updateVisibility(rawContactId);
17260be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            return count;
17270be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        }
17280be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
17290be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        private void updateVisibility(long rawContactId) {
1730b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            long contactId = mDbHelper.getContactId(rawContactId);
17310be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            if (contactId != 0) {
1732b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                mDbHelper.updateContactVisible(contactId);
17330be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov            }
1734653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1735653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1736653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        private void resolveGroupSourceIdInValues(long rawContactId, SQLiteDatabase db,
1737653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                ContentValues values, boolean isInsert) {
1738653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
1739653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
1740653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId && containsGroupId) {
1741653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                throw new IllegalArgumentException(
1742653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
1743653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                + "and GroupMembership.GROUP_ROW_ID");
1744653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1745653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1746653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (!containsGroupSourceId && !containsGroupId) {
1747653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                if (isInsert) {
1748653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    throw new IllegalArgumentException(
1749653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
1750653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                    + "and GroupMembership.GROUP_ROW_ID");
1751653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                } else {
1752653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                    return;
1753653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                }
1754653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1755653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1756653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            if (containsGroupSourceId) {
1757653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
1758ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
1759ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        mInsertedRawContacts.get(rawContactId));
1760653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.remove(GroupMembership.GROUP_SOURCE_ID);
1761653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                values.put(GroupMembership.GROUP_ROW_ID, groupId);
1762653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            }
1763653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        }
1764653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    }
1765653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1766a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    public class PhotoDataRowHandler extends DataRowHandler {
1767a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1768a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public PhotoDataRowHandler() {
1769a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            super(Photo.CONTENT_ITEM_TYPE);
1770a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1771a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1772a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1773a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public long insert(SQLiteDatabase db, long rawContactId, ContentValues values) {
1774a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long dataId = super.insert(db, rawContactId, values);
1775285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (!isNewRawContact(rawContactId)) {
1776285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov                mContactAggregator.updatePhotoId(db, rawContactId);
1777285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
1778a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return dataId;
1779a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1780a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1781a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1782813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        public boolean update(SQLiteDatabase db, ContentValues values, Cursor c,
1783f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                boolean callerIsSyncAdapter) {
1784a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
1785813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            if (!super.update(db, values, c, callerIsSyncAdapter)) {
1786813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov                return false;
1787813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            }
1788813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov
1789a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1790813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return true;
1791a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1792a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1793a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        @Override
1794a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        public int delete(SQLiteDatabase db, Cursor c) {
1795a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
1796a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            int count = super.delete(db, c);
1797a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            mContactAggregator.updatePhotoId(db, rawContactId);
1798a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            return count;
1799a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
1800a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov    }
1801a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
1802ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    /**
1803ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * An entry in group id cache. It maps the combination of (account type, account name
1804ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     * and source id) to group row id.
1805ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov     */
1806ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    public class GroupIdCacheEntry {
1807ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType;
1808ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName;
1809ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String sourceId;
1810ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        long groupId;
1811ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    }
1812a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov
18133cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private HashMap<String, DataRowHandler> mDataRowHandlers;
1814b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private ContactsDatabaseHelper mDbHelper;
181531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
18164097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
1817f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private NameLookupBuilder mNameLookupBuilder;
1818315dd702d006aedf2f867d3fe49e31e05e4f9a16Dmitri Plotnikov
1819622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private PostalSplitter mPostalSplitter;
1820622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
1821ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // We don't need a soft cache for groups - the assumption is that there will only
1822ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // be a small number of contact groups. The cache is keyed off source id.  The value
1823ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    // is a list of groups with this group id.
1824ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache = Maps.newHashMap();
1825ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
182672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private ContactDirectoryManager mContactDirectoryManager;
1827622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    private ContactAggregator mContactAggregator;
1828f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
1829a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov    private GlobalSearchSupport mGlobalSearchSupport;
1830d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    private CommonNicknameCache mCommonNicknameCache;
1831a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
183220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    private ContentValues mValues = new ContentValues();
18331129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private CharArrayBuffer mCharArrayBuffer = new CharArrayBuffer(128);
18345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private NameSplitter.Name mName = new NameSplitter.Name();
183573f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    private HashMap<String, Boolean> mAccountWritability = Maps.newHashMap();
183620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
183709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
183809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private long mEstimatedStorageRequirement = 0;
1839ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov    private volatile CountDownLatch mAccessLatch;
184073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
1841ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private HashMap<Long, Account> mInsertedRawContacts = Maps.newHashMap();
1842b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashSet<Long> mUpdatedRawContacts = Sets.newHashSet();
1843a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private HashSet<Long> mDirtyRawContacts = Sets.newHashSet();
1844b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private HashMap<Long, Object> mUpdatedSyncStates = Maps.newHashMap();
1845de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov
18461a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey    private boolean mVisibleTouched = false;
18471a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
184881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    private boolean mSyncToNetwork;
184981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
18504cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    private Locale mCurrentLocale;
1851d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
18522530512f639c4979fd7371c7dd25dd67e8118124Bai Tao    private CountryMonitor mCountryMonitor;
185373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
185472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
18554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
18564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
1857de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        super.onCreate();
1858ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        try {
1859ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return initialize();
1860ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        } catch (RuntimeException e) {
1861ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            Log.e(TAG, "Cannot start provider", e);
1862ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov            return false;
1863ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov        }
1864ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    }
186535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1866ea029fd79225640e49be82457b83b6b3a0279fd0Dmitri Plotnikov    private boolean initialize() {
1867de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final Context context = getContext();
18682530512f639c4979fd7371c7dd25dd67e8118124Bai Tao        mCountryMonitor = CountryMonitor.getInstance(context);
1869b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper();
187072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager = new ContactDirectoryManager(this);
1871a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov        mGlobalSearchSupport = new GlobalSearchSupport(this);
1872b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport);
1873b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDb = mDbHelper.getWritableDatabase();
1874653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
187551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
1876d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
1877b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mSetPrimaryStatement = mDb.compileStatement(
1878653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1879653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_PRIMARY + "=(_id=?)" +
1880653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1881653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + "=?");
1882653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1883b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mSetSuperPrimaryStatement = mDb.compileStatement(
1884653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "UPDATE " + Tables.DATA +
1885653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " SET " + Data.IS_SUPER_PRIMARY + "=(" + Data._ID + "=?)" +
1886653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                " WHERE " + DataColumns.MIMETYPE_ID + "=?" +
1887653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                "   AND " + Data.RAW_CONTACT_ID + " IN (" +
1888653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        "SELECT " + RawContacts._ID +
1889653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1890653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + " =(" +
1891653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                "SELECT " + RawContacts.CONTACT_ID +
1892653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " FROM " + Tables.RAW_CONTACTS +
1893653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                                " WHERE " + RawContacts._ID + "=?))");
1894653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
1895b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mRawContactDisplayNameUpdate = mDb.compileStatement(
189625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
18975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                " SET " +
18985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_SOURCE + "=?," +
18995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_PRIMARY + "=?," +
19005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.DISPLAY_NAME_ALTERNATIVE + "=?," +
19015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.PHONETIC_NAME + "=?," +
19025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.PHONETIC_NAME_STYLE + "=?," +
19035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.SORT_KEY_PRIMARY + "=?," +
19045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        RawContacts.SORT_KEY_ALTERNATIVE + "=?" +
190525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov                " WHERE " + RawContacts._ID + "=?");
19063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
1907b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mLastStatusUpdate = mDb.compileStatement(
1908a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                "UPDATE " + Tables.CONTACTS +
1909a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
1910a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "(SELECT " + DataColumns.CONCRETE_ID +
1911a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " FROM " + Tables.STATUS_UPDATES +
1912a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.DATA +
1913a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + StatusUpdatesColumns.DATA_ID + "="
1914a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + DataColumns.CONCRETE_ID + ")" +
1915a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " JOIN " + Tables.RAW_CONTACTS +
1916a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
1917a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                                + RawContactsColumns.CONCRETE_ID + ")" +
1918a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        " WHERE " + RawContacts.CONTACT_ID + "=?" +
19190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
19200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                                + StatusUpdates.STATUS +
1921a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                        " LIMIT 1)" +
1922a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                " WHERE " + ContactsColumns.CONCRETE_ID + "=?");
1923e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
1924b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mNameLookupInsert = mDb.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
1925f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
1926f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
1927f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + ") VALUES (?,?,?,?)");
1928b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mNameLookupDelete = mDb.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
1929f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                + NameLookupColumns.DATA_ID + "=?");
1930f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1931b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateInsert = mDb.compileStatement(
1932a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT INTO " + Tables.STATUS_UPDATES + "("
1933a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
19340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
19350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
19360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
19370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
19380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?)");
1939a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1940b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateReplace = mDb.compileStatement(
1941a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "INSERT OR REPLACE INTO " + Tables.STATUS_UPDATES + "("
1942a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                        + StatusUpdatesColumns.DATA_ID + ", "
19430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_TIMESTAMP + ","
19440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + ","
19450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_RES_PACKAGE + ","
19460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + ","
19470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + ")" +
19480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " VALUES (?,?,?,?,?,?)");
1949a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1950b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateAutoTimestamp = mDb.compileStatement(
1951a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
19520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_TIMESTAMP + "=?,"
19530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS + "=?" +
1954a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?"
19550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + " AND " + StatusUpdates.STATUS + "!=?");
19560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
1957b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusAttributionUpdate = mDb.compileStatement(
19580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                "UPDATE " + Tables.STATUS_UPDATES +
19590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " SET " + StatusUpdates.STATUS_RES_PACKAGE + "=?,"
19600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_ICON + "=?,"
19610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                        + StatusUpdates.STATUS_LABEL + "=?" +
19620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1963a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1964b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mStatusUpdateDelete = mDb.compileStatement(
1965a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                "DELETE FROM " + Tables.STATUS_UPDATES +
1966a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                " WHERE " + StatusUpdatesColumns.DATA_ID + "=?");
1967a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1968f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        // When setting NAME_VERIFIED to 1 on a raw contact, reset it to 0
1969f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov        // on all other raw contacts in the same aggregate
1970b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mResetNameVerifiedForOtherRawContacts = mDb.compileStatement(
1971f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                "UPDATE " + Tables.RAW_CONTACTS +
1972f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " SET " + RawContacts.NAME_VERIFIED + "=0" +
1973f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " WHERE " + RawContacts.CONTACT_ID + "=(" +
1974f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        "SELECT " + RawContacts.CONTACT_ID +
1975f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        " FROM " + Tables.RAW_CONTACTS +
1976f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                        " WHERE " + RawContacts._ID + "=?)" +
1977f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                " AND " + RawContacts._ID + "!=?");
1978f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
1979f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
1980f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mMimeTypeIdIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
19811129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdStructuredName = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE);
19821129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdOrganization = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE);
19831129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdNickname = mDbHelper.getMimeTypeId(Nickname.CONTENT_ITEM_TYPE);
19841129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mMimeTypeIdPhone = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
198504b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov
1986bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        verifyAccounts();
1987bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
198865ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        if (isLegacyContactImportNeeded()) {
198965ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov            importLegacyContactsAsync();
199080952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov        } else {
199180952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov            verifyLocale();
199265ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov        }
199365ce381c2bb7ddcc3e7d3b8f5f7095831be97603Dmitri Plotnikov
199472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        startContactDirectoryManager();
199572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
1996b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return (mDb != null);
19974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
19984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
19992530512f639c4979fd7371c7dd25dd67e8118124Bai Tao    protected String getCurrentCountryIso() {
20002530512f639c4979fd7371c7dd25dd67e8118124Bai Tao        return mCountryMonitor.getCountryIso();
2001892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov    }
2002892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov
2003ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao    private void initDataRowHandlers() {
2004ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers = new HashMap<String, DataRowHandler>();
2005ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao
2006ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
2007ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Im.CONTENT_ITEM_TYPE,
2008ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new CommonDataRowHandler(Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL));
2009ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new CommonDataRowHandler(
2010ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL));
2011ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Organization.CONTENT_ITEM_TYPE, new OrganizationDataRowHandler());
2012ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Phone.CONTENT_ITEM_TYPE, new PhoneDataRowHandler());
2013ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Nickname.CONTENT_ITEM_TYPE, new NicknameDataRowHandler());
2014ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(StructuredName.CONTENT_ITEM_TYPE,
2015ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new StructuredNameRowHandler(mNameSplitter));
2016ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(StructuredPostal.CONTENT_ITEM_TYPE,
2017ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao              new StructuredPostalRowHandler(mPostalSplitter));
2018ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(GroupMembership.CONTENT_ITEM_TYPE, new GroupMembershipRowHandler());
2019ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao      mDataRowHandlers.put(Photo.CONTENT_ITEM_TYPE, new PhotoDataRowHandler());
2020ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao    }
202172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
202251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
2023767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov     * Visible for testing.
2024767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov     */
2025767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /* package */ PhotoPriorityResolver createPhotoPriorityResolver(Context context) {
2026767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov        return new PhotoPriorityResolver(context);
2027767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    }
2028767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov
2029767e109d6f08749b9ed202c0b71f3459eaae2115Dmitri Plotnikov    /**
203051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * (Re)allocates all locale-sensitive structures.
203151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
203204b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov    private void initForDefaultLocale() {
20334cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mCurrentLocale = getLocale();
203404b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        mNameSplitter = mDbHelper.createNameSplitter();
20354cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
20364cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        mPostalSplitter = new PostalSplitter(mCurrentLocale);
203751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mCommonNicknameCache = new CommonNicknameCache(mDbHelper.getReadableDatabase());
2038cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        ContactLocaleUtils.getIntance().setLocale(mCurrentLocale);
20395b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator = new ContactAggregator(this, mDbHelper,
20405b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                createPhotoPriorityResolver(getContext()), mNameSplitter, mCommonNicknameCache);
20415b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov        mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true));
20425b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
2043ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao        initDataRowHandlers();
20444cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
20454cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
204653fac8f99f3884c372c907a76766d27fa9e1d95fDmitri Plotnikov    public void onLocaleChanged() {
20474f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        if (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
20484f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov            return;
20494f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov        }
20504f20a360a2f0a7a83900c28fc7728542b38d8939Dmitri Plotnikov
205151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        initForDefaultLocale();
205251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        verifyLocale();
20534cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao    }
205451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2055c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    protected void verifyAccounts() {
2056c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        AccountManager.get(getContext()).addOnAccountsUpdatedListener(this, null, false);
2057c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov        onAccountsUpdated(AccountManager.get(getContext()).getAccounts());
2058c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov    }
2059c1778ef6fa53b6bf08fd715b3ad70c052c5f1ce9Dmitri Plotnikov
206051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    /**
206151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * Verifies that the contacts database is properly configured for the current locale.
206251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * If not, changes the database locale to the current locale using an asynchronous task.
206351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * This needs to be done asynchronously because the process involves rebuilding
206451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * large data structures (name lookup, sort keys), which can take minutes on
206551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     * a large set of contacts.
206651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov     */
206751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void verifyLocale() {
2068f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2069f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        // The process is already running - postpone the change
2070f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        if (mProviderStatus == ProviderStatus.STATUS_CHANGING_LOCALE) {
2071f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov            return;
2072f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov        }
2073f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
207451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
207551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final String providerLocale = prefs.getString(PREF_LOCALE, null);
207651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        final Locale currentLocale = mCurrentLocale;
207751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        if (currentLocale.toString().equals(providerLocale)) {
207851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            return;
207951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        }
208051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
208151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        int providerStatus = mProviderStatus;
208251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_CHANGING_LOCALE);
208351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
208451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        AsyncTask<Integer, Void, Void> task = new AsyncTask<Integer, Void, Void>() {
208551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
208651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            int savedProviderStatus;
208751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
208851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
208951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected Void doInBackground(Integer... params) {
209051f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                savedProviderStatus = params[0];
209151f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, currentLocale);
209251f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                return null;
209351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
209451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
209551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            @Override
209651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            protected void onPostExecute(Void result) {
20979516b6eef76b3504913f5efcadf603969946a3d0Brad Fitzpatrick                prefs.edit().putString(PREF_LOCALE, currentLocale.toString()).apply();
209851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                setProviderStatus(savedProviderStatus);
2099f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov
2100f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // Recursive invocation, needed to cover the case where locale
2101f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                // changes once and then changes again before the db upgrade is completed.
2102f0da835940ab6ae1aa37e0ba2ddd29c3117eb212Dmitri Plotnikov                verifyLocale();
210351f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov            }
210451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        };
210551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
210651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        task.execute(providerStatus);
210751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
210851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
210931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
2110de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    @Override
2111b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
2112b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        return ContactsDatabaseHelper.getInstance(context);
211331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
211431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
2115013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    /* package */ NameSplitter getNameSplitter() {
2116013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov        return mNameSplitter;
2117013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov    }
2118013a0d6b3d392fb49d4618f2527b2ed3fec7d34fDmitri Plotnikov
21195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    /* Visible for testing */
212072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public ContactDirectoryManager getContactDirectoryManager() {
212172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return mContactDirectoryManager;
212272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
212372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
212472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
21255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    protected Locale getLocale() {
21265dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        return Locale.getDefault();
21275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
21285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
212972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /* Visible for testing */
213072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    protected void startContactDirectoryManager() {
213172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        getContactDirectoryManager().start();
213272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
213372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
21343d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    protected boolean isLegacyContactImportNeeded() {
2135b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        int version = Integer.parseInt(mDbHelper.getProperty(PROPERTY_CONTACTS_IMPORTED, "0"));
2136b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        return version < PROPERTY_CONTACTS_IMPORT_VERSION;
21373d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
21383d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2139568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    protected LegacyContactImporter getLegacyContactImporter() {
2140568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return new LegacyContactImporter(getContext(), this);
2141568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2142568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2143568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2144568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * Imports legacy contacts in a separate thread.  As long as the import process is running
2145568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * all other access to the contacts is blocked.
2146568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2147568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void importLegacyContactsAsync() {
2148bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Importing legacy contacts");
2149bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADING);
2150bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch == null) {
2151bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            mAccessLatch = new CountDownLatch(1);
2152bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2153568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2154568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        Thread importThread = new Thread("LegacyContactImport") {
2155568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            @Override
2156568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            public void run() {
215780952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                final SharedPreferences prefs =
215880952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                    PreferenceManager.getDefaultSharedPreferences(getContext());
215980952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                mDbHelper.setLocale(ContactsProvider2.this, mCurrentLocale);
216080952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov                prefs.edit().putString(PREF_LOCALE, mCurrentLocale.toString()).commit();
216180952e03e425a04ea2fd77e3ff44a8453ffdefe1Dmitri Plotnikov
2162bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                LegacyContactImporter importer = getLegacyContactImporter();
2163bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (importLegacyContacts(importer)) {
2164bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportSuccess();
2165bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2166bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    onLegacyContactImportFailure();
2167568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov                }
2168568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            }
2169568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        };
2170568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2171568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        importThread.start();
2172568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2173568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2174bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2175bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Unlocks the provider and declares that the import process is complete.
2176bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2177bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportSuccess() {
2178bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2179bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)getContext().getSystemService(Context.NOTIFICATION_SERVICE);
2180bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.cancel(LEGACY_IMPORT_FAILED_NOTIFICATION);
2181bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2182b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        // Store a property in the database indicating that the conversion process succeeded
2183b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov        mDbHelper.setProperty(PROPERTY_CONTACTS_IMPORTED,
2184b2e27298ae54ec2215eadf98ecc100aedba98d1aDmitri Plotnikov                String.valueOf(PROPERTY_CONTACTS_IMPORT_VERSION));
2185bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_NORMAL);
2186bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch.countDown();
2187bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mAccessLatch = null;
2188bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Completed import of legacy contacts");
2189bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    }
2190bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2191bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    /**
2192bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     * Announces the provider status and keeps the provider locked.
2193bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov     */
2194bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov    private void onLegacyContactImportFailure() {
2195bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Context context = getContext();
2196bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        NotificationManager nm =
2197bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
2198bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2199bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        // Show a notification
2200bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Notification n = new Notification(android.R.drawable.stat_notify_error,
2201bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_ticker),
2202bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                System.currentTimeMillis());
2203bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.setLatestEventInfo(context,
2204bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_title),
2205bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                context.getString(R.string.upgrade_out_of_memory_notification_text),
2206bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                PendingIntent.getActivity(context, 0, new Intent(Intents.UI.LIST_DEFAULT), 0));
2207bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        n.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
2208bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2209bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        nm.notify(LEGACY_IMPORT_FAILED_NOTIFICATION, n);
2210bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2211bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        setProviderStatus(ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY);
2212bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        Log.v(TAG, "Failed to import legacy contacts");
22133d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
22143d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
22153d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    /* Visible for testing */
2216568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /* package */ boolean importLegacyContacts(LegacyContactImporter importer) {
22170e8520cf7f15576ce4d66202203770086fd26a71Ken Shirriff        boolean aggregatorEnabled = mContactAggregator.isEnabled();
22183d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        mContactAggregator.setEnabled(false);
22193d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        try {
2220bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (importer.importContacts()) {
2221bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov
2222bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                // TODO aggregate all newly added raw contacts
2223bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                mContactAggregator.setEnabled(aggregatorEnabled);
2224bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                return true;
2225bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
22263d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        } catch (Throwable e) {
22273d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov           Log.e(TAG, "Legacy contact import failed", e);
22283d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
2229bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        mEstimatedStorageRequirement = importer.getEstimatedStorageRequirement();
2230bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        return false;
22313d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov    }
22323d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2233a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
2234a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
2235a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
2236a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
2237b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mDbHelper.wipeData();
2238a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
2239a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2240568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    /**
2241568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * While importing and aggregating contacts, this content provider will
2242568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * block all attempts to change contacts data. In particular, it will hold
2243568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * up all contact syncs. As soon as the import process is complete, all
2244568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * processes waiting to write to the provider are unblocked and can proceed
2245568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     * to compete for the database transaction monitor.
2246568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov     */
2247568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    private void waitForAccess() {
2248ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        CountDownLatch latch = mAccessLatch;
2249ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov        if (latch != null) {
2250ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            while (true) {
2251ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                try {
2252ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    latch.await();
2253ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    mAccessLatch = null;
2254ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                    return;
2255ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                } catch (InterruptedException e) {
225681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    Thread.currentThread().interrupt();
2257ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov                }
2258ff065d085b794a0bf4be4cf6e87a67bf060e0319Dmitri Plotnikov            }
2259568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        }
2260568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2261568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2262568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2263568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public Uri insert(Uri uri, ContentValues values) {
2264568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2265568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.insert(uri, values);
2266568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2267568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2268568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2269568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
2270bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        if (mAccessLatch != null) {
2271bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // We are stuck trying to upgrade contacts db.  The only update request
2272bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // allowed in this case is an update of provider status, which will trigger
2273bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            // an attempt to upgrade contacts again.
2274bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            int match = sUriMatcher.match(uri);
2275bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            if (match == PROVIDER_STATUS && isLegacyContactImportNeeded()) {
2276bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                Integer newStatus = values.getAsInteger(ProviderStatus.STATUS);
2277bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                if (newStatus != null && newStatus == ProviderStatus.STATUS_UPGRADING) {
2278bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    importLegacyContactsAsync();
2279bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 1;
2280bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                } else {
2281bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                    return 0;
2282bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov                }
2283bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov            }
2284bd578a748ab5bd74aa63511cce8769d5882f4651Dmitri Plotnikov        }
2285568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2286568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.update(uri, values, selection, selectionArgs);
2287568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2288568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2289568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2290568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public int delete(Uri uri, String selection, String[] selectionArgs) {
2291568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2292568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.delete(uri, selection, selectionArgs);
2293568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2294568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
2295568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    @Override
2296568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2297568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov            throws OperationApplicationException {
2298568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        waitForAccess();
2299568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov        return super.applyBatch(operations);
2300568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov    }
2301568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
23024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2303285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void onBeginTransaction() {
2304bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2305b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "onBeginTransaction");
2306b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2307285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.onBeginTransaction();
23081ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.clearPendingAggregations();
2309b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2310b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2311b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2312b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void clearTransactionalChanges() {
2313285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        mInsertedRawContacts.clear();
2314b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.clear();
2315df9db5e99572ce9760eb265683134c1f3293928fFred Quintana        mUpdatedSyncStates.clear();
2316a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.clear();
2317285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2318285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2319285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2320285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    protected void beforeTransactionCommit() {
23211129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
2322bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2323b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "beforeTransactionCommit");
2324b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2325285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        super.beforeTransactionCommit();
2326b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
23271ea29714ef8fdd62b71f265f27391c22f4a50340Fred Quintana        mContactAggregator.aggregateInTransaction(mDb);
23281a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (mVisibleTouched) {
23291a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = false;
2330b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.updateAllVisible();
23311a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        }
2332b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2333b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2334b5a4add17815167d20a90645779df34cdf45280dFred Quintana    private void flushTransactionalChanges() {
2335bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
2336b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "flushTransactionChanges");
2337b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
23381129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
233908e42c9c153a60bf2e7c71dd40bf84bb5fc93555Dmitri Plotnikov        for (long rawContactId : mInsertedRawContacts.keySet()) {
2340d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            updateRawContactDisplayName(mDb, rawContactId);
2341d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            mContactAggregator.onRawContactInsert(mDb, rawContactId);
2342285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        }
2343b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2344a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        if (!mDirtyRawContacts.isEmpty()) {
2345a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2346a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_DIRTY_SQL);
2347a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mDirtyRawContacts);
2348a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2349a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2350a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        }
2351a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2352b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (!mUpdatedRawContacts.isEmpty()) {
2353a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.setLength(0);
2354a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(UPDATE_RAW_CONTACT_SET_VERSION_SQL);
2355a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            appendIds(mSb, mUpdatedRawContacts);
2356a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mSb.append(")");
2357a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            mDb.execSQL(mSb.toString());
2358b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2359b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2360b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (Map.Entry<Long, Object> entry : mUpdatedSyncStates.entrySet()) {
2361b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long id = entry.getKey();
23629d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            if (mDbHelper.getSyncState().update(mDb, id, entry.getValue()) <= 0) {
23639d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                throw new IllegalStateException(
23649d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana                        "unable to update sync state, does it still exist?");
23659d9673d6a93926c337e23b7e2dcfb9aebc43e9abFred Quintana            }
2366b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2367b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2368b5a4add17815167d20a90645779df34cdf45280dFred Quintana        clearTransactionalChanges();
2369b5a4add17815167d20a90645779df34cdf45280dFred Quintana    }
2370b5a4add17815167d20a90645779df34cdf45280dFred Quintana
2371a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    /**
2372a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * Appends comma separated ids.
2373a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     * @param ids Should not be empty
2374a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov     */
2375a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov    private void appendIds(StringBuilder sb, HashSet<Long> ids) {
2376b5a4add17815167d20a90645779df34cdf45280dFred Quintana        for (long id : ids) {
2377a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov            sb.append(id).append(',');
2378b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2379a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov
2380a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        sb.setLength(sb.length() - 1); // Yank the last comma
2381285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2382285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2383285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    @Override
2384cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    protected void notifyChange() {
238581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        notifyChange(mSyncToNetwork);
238681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = false;
238781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
238881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
238981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    protected void notifyChange(boolean syncToNetwork) {
239081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
239181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                syncToNetwork);
2392cdbd854decda3f493b395c8867f2cd131d95d09fDmitri Plotnikov    }
2393568904d1cc9acfabac78b6fcbf8a7d5115688174Dmitri Plotnikov
239451f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    protected void setProviderStatus(int status) {
239551f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        mProviderStatus = status;
239651f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov        getContext().getContentResolver().notifyChange(ContactsContract.ProviderStatus.CONTENT_URI,
239751f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov                null, false);
239851f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov    }
239951f41be3b905c63ccffcdc82ec58cf5f7ded2c34Dmitri Plotnikov
2400285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    private boolean isNewRawContact(long rawContactId) {
2401ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return mInsertedRawContacts.containsKey(rawContactId);
2402285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov    }
2403285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
24043cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    private DataRowHandler getDataRowHandler(final String mimeType) {
24053cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        DataRowHandler handler = mDataRowHandlers.get(mimeType);
24063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        if (handler == null) {
24073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            handler = new CustomDataRowHandler(mimeType);
24083cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov            mDataRowHandlers.put(mimeType, handler);
24093cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        }
24103cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov        return handler;
24113cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
24123cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
24134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
2414de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected Uri insertInTransaction(Uri uri, ContentValues values) {
2415bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
24161129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Log.v(TAG, "insertInTransaction: " + uri + " " + values);
2417b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
2418f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2419f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
2420f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
2421f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana
2422a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
2423a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
242435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2425a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
242635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
2427b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                id = mDbHelper.getSyncState().insert(mDb, values);
242835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
242935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
2430d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
2431d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                insertContact(values);
24326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
24336bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
24346bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
24355ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
2436dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                id = insertRawContact(uri, values, callerIsSyncAdapter);
2437f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2438a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2439a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2440a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24415ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
24425ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                values.put(Data.RAW_CONTACT_ID, uri.getPathSegments().get(1));
2443f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2444f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2445a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2446a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2447a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2448a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
2449f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                id = insertData(values, callerIsSyncAdapter);
2450f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2451a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
2452a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
2453a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2454ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
2455f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                id = insertGroup(uri, values, callerIsSyncAdapter);
2456f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
2457ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
2458ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
2459ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
2460eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
24615aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                id = insertSettings(uri, values);
246243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
2463eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
2464eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
2465eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
246682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
246782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                id = insertStatusUpdate(values);
24681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
24691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
24701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2471a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
247281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
2473f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
2474a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2475a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
24767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
24777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
24787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
24797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
2480de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        return ContentUris.withAppendedId(uri, id);
2481a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2482a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2483a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2484e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * If account is non-null then store it in the values. If the account is
2485e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * already specified in the values then it must be consistent with the
2486e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * account, if it is non-null.
2487e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *
2488e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param uri Current {@link Uri} being operated on.
2489e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @param values {@link ContentValues} to read and possibly update.
2490e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when only one of
2491e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_NAME} or
2492e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             {@link RawContacts#ACCOUNT_TYPE} is specified, leaving the
2493e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             other undefined.
2494e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     * @throws IllegalArgumentException when {@link RawContacts#ACCOUNT_NAME}
2495e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             and {@link RawContacts#ACCOUNT_TYPE} are inconsistent between
2496e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey     *             the given {@link Uri} and {@link ContentValues}.
24977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
2498e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    private Account resolveAccount(Uri uri, ContentValues values) throws IllegalArgumentException {
2499f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
2500f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
2501e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
2502f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2503f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
2504f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String valueAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
2505e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialValues = TextUtils.isEmpty(valueAccountName)
2506e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                ^ TextUtils.isEmpty(valueAccountType);
2507e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2508e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri || partialValues) {
2509e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
2510fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2511fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
2512e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
2513e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2514e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
2515e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
2516e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validUri = !TextUtils.isEmpty(accountName);
2517e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validValues = !TextUtils.isEmpty(valueAccountName);
2518e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2519e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validValues && validUri) {
2520e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Check that accounts match when both present
2521e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            final boolean accountMatch = TextUtils.equals(accountName, valueAccountName)
2522e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    && TextUtils.equals(accountType, valueAccountType);
2523e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (!accountMatch) {
2524fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new IllegalArgumentException(mDbHelper.exceptionMessage(
2525fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        "When both specified, ACCOUNT_NAME and ACCOUNT_TYPE must match", uri));
2526e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            }
2527e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validUri) {
2528e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Fill values from Uri when not present
2529f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_NAME, accountName);
2530f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            values.put(RawContacts.ACCOUNT_TYPE, accountType);
2531e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else if (validValues) {
2532f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountName = valueAccountName;
2533f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            accountType = valueAccountType;
2534e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } else {
2535e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            return null;
2536f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
2537f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2538e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Use cached Account object when matches, otherwise create
2539f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mAccount == null
2540f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.name.equals(accountName)
2541f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                || !mAccount.type.equals(accountType)) {
2542f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mAccount = new Account(accountName, accountType);
2543035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
2544f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2545e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        return mAccount;
25467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
25477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
2549d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov     * Inserts an item in the contacts table
25506bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
25516bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
25526bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
25536bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
2554d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    private long insertContact(ContentValues values) {
2555de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregate contacts are created automatically");
25566bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
25576bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
25586bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
2559a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
2560a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2561f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param uri the values for the new row
2562f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * @param values the account this contact should be associated with. may be null.
2563dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana     * @param callerIsSyncAdapter
2564a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2565a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2566dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private long insertRawContact(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
2567f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
2568f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
2569f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
2570f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2571e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
25727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
25733d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        if (values.containsKey(RawContacts.DELETED)
25743d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov                && values.getAsInteger(RawContacts.DELETED) != 0) {
2575f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
25763d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov        }
25773d8b043c3341a5b6c2e781b7eba9767d5cd13267Dmitri Plotnikov
2578f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long rawContactId = mDb.insert(Tables.RAW_CONTACTS, RawContacts.CONTACT_ID, mValues);
2579f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT;
2580f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        if (mValues.containsKey(RawContacts.AGGREGATION_MODE)) {
2581f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            aggregationMode = mValues.getAsInteger(RawContacts.AGGREGATION_MODE);
2582f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        }
2583f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov        mContactAggregator.markNewForAggregation(rawContactId, aggregationMode);
2584285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov
2585285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov        // Trigger creation of a Contact based on this RawContact at the end of transaction
2586e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mInsertedRawContacts.put(rawContactId, account);
2587f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2588dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter) {
2589dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            addAutoAddMembership(rawContactId);
2590dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            final Long starred = values.getAsLong(RawContacts.STARRED);
2591dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (starred != null && starred != 0) {
2592dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateFavoritesMembership(rawContactId, starred != 0);
2593dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2594dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2595dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2596023b9437a3644e59309b8cfd12c6d84b98433f95Dmitri Plotnikov        return rawContactId;
2597a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
2598a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
2599dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void addAutoAddMembership(long rawContactId) {
2600dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_AUTO_ADD_GROUPS_BY_RAW_CONTACT_ID,
2601dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2602dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2603dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            insertDataGroupMembership(rawContactId, groupId);
2604dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2605dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2606dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2607dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private Long findGroupByRawContactId(String selection, long rawContactId) {
2608dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        Cursor c = mDb.query(Tables.GROUPS + "," + Tables.RAW_CONTACTS, PROJECTION_GROUP_ID,
2609dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection,
2610dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                new String[]{Long.toString(rawContactId)},
2611dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                null /* groupBy */, null /* having */, null /* orderBy */);
2612dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        try {
2613dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            while (c.moveToNext()) {
2614dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return c.getLong(0);
2615dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2616dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return null;
2617dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        } finally {
2618dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            c.close();
2619dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2620dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2621dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2622dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void updateFavoritesMembership(long rawContactId, boolean isStarred) {
2623dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final Long groupId = findGroupByRawContactId(SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID,
2624dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                rawContactId);
2625dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (groupId != null) {
2626dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (isStarred) {
2627dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                insertDataGroupMembership(rawContactId, groupId);
2628dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
2629dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                deleteDataGroupMembership(rawContactId, groupId);
2630dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
2631dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
2632dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2633dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2634dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void insertDataGroupMembership(long rawContactId, long groupId) {
2635dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        ContentValues groupMembershipValues = new ContentValues();
2636dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.GROUP_ROW_ID, groupId);
2637dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
2638dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        groupMembershipValues.put(DataColumns.MIMETYPE_ID,
2639dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
2640dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.insert(Tables.DATA, null, groupMembershipValues);
2641dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2642dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2643dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void deleteDataGroupMembership(long rawContactId, long groupId) {
2644dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final String[] selectionArgs = {
2645dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)),
2646dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(groupId),
2647dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                Long.toString(rawContactId)};
2648dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        mDb.delete(Tables.DATA, SELECTION_GROUPMEMBERSHIP_DATA, selectionArgs);
2649dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    }
2650dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
2651a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
2652a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
2653a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
2654a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
2655a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
2656a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
2657f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
2658a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
2659de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.clear();
2660de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.putAll(values);
266167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey
2662de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
266320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
2664de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace package with internal mapping
2665de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String packageName = mValues.getAsString(Data.RES_PACKAGE);
2666de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (packageName != null) {
2667b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
2668de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
2669de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.RES_PACKAGE);
2670508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
2671de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Replace mimetype with internal mapping
2672de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        final String mimeType = mValues.getAsString(Data.MIMETYPE);
2673de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        if (TextUtils.isEmpty(mimeType)) {
2674de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            throw new IllegalArgumentException(Data.MIMETYPE + " is required");
2675de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        }
26764097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
2677b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType));
2678de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
2679a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2680a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
2681a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        id = rowHandler.insert(mDb, rawContactId, mValues);
2682f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
2683de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            setRawContactDirty(rawContactId);
2684a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
2685b5a4add17815167d20a90645779df34cdf45280dFred Quintana        mUpdatedRawContacts.add(rawContactId);
2686a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
26874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
26884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
26898e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov    private void triggerAggregation(long rawContactId) {
26908e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        if (!mContactAggregator.isEnabled()) {
26918e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            return;
26928e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        }
26938e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
2694b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
2695f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
26968e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DISABLED:
26978e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                break;
26988e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
26998e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_DEFAULT: {
270069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
2701f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
27028e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
27038e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
27048e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
2705b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
2706f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
27078e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                if (contactId != 0) {
27088e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
27098e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                }
2710f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
27118e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2712f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2713c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
2714b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                long contactId = mDbHelper.getContactId(rawContactId);
27158e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                mContactAggregator.aggregateContact(mDb, rawContactId, contactId);
2716f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
27178e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov            }
2718f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
2719f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
2720f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
2721a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
27225ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * Returns the group id of the group with sourceId and the same account as rawContactId.
27239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
27249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
27255ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov     * @param rawContactId the contact this group is associated with
27269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
27279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
27289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
27299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
27309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
2731ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov    private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
2732ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Account account) {
2733ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2734ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (account == null) {
27354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs1[0] = String.valueOf(rawContactId);
2736ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
27374da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
2738ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            try {
2739ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                if (c.moveToFirst()) {
2740ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
2741ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
2742ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
2743ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        account = new Account(accountName, accountType);
2744ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    }
27459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2746ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            } finally {
2747ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                c.close();
27489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
27499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2750ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
27519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
27529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
2753ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    + "has a sourceid the the contact must be associated with "
27549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
27559261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
27569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2757ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
2758ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        if (entries == null) {
2759ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            entries = new ArrayList<GroupIdCacheEntry>(1);
2760ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            mGroupIdCache.put(sourceId, entries);
2761ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2762ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2763ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        int count = entries.size();
2764ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        for (int i = 0; i < count; i++) {
2765ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            GroupIdCacheEntry entry = entries.get(i);
2766ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (entry.accountName.equals(account.name) && entry.accountType.equals(account.type)) {
2767ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                return entry.groupId;
2768ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            }
2769ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        }
2770ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2771ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        GroupIdCacheEntry entry = new GroupIdCacheEntry();
2772ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountName = account.name;
2773ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.accountType = account.type;
2774ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entry.sourceId = sourceId;
2775ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        entries.add(0, entry);
2776ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
27779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
27785ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        // as the contact refered to by rawContactId
2779ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        Cursor c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
27809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
2781df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                new String[]{sourceId, account.name, account.type}, null, null, null);
27829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
2783ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov            if (c.moveToFirst()) {
2784ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = c.getLong(0);
27859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
27869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
2787df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.name);
2788df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.type);
27899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
27909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
27919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
27929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
27939261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
27949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
2795ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                entry.groupId = groupId;
27969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
27979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
27989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
27999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2800ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
2801ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        return entry.groupId;
28029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
28039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2804d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    private interface DisplayNameQuery {
28051129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        public static final String RAW_SQL =
28061129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                "SELECT "
28071129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + DataColumns.MIMETYPE_ID + ","
28081129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.IS_PRIMARY + ","
28091129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        + Data.DATA1 + ","
28105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA2 + ","
28115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA3 + ","
28125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA4 + ","
28135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA5 + ","
28145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA6 + ","
28155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA7 + ","
28165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA8 + ","
28175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA9 + ","
28185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA10 + ","
28195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        + Data.DATA11 +
28201129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " FROM " + Tables.DATA +
28211129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                " WHERE " + Data.RAW_CONTACT_ID + "=?" +
28221129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        " AND (" + Data.DATA1 + " NOT NULL OR " +
28231129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                Organization.TITLE + " NOT NULL)";
2824d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2825d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int MIMETYPE = 0;
2826d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        public static final int IS_PRIMARY = 1;
28275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int DATA1 = 2;
28285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int GIVEN_NAME = 3;                         // data2
28295dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int FAMILY_NAME = 4;                        // data3
28305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PREFIX = 5;                             // data4
28315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int TITLE = 5;                              // data4
28325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int MIDDLE_NAME = 6;                        // data5
28335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int SUFFIX = 7;                             // data6
28345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_GIVEN_NAME = 8;                // data7
28355dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_MIDDLE_NAME = 9;               // data8
28365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int ORGANIZATION_PHONETIC_NAME = 9;         // data8
28375dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_FAMILY_NAME = 10;              // data9
28385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int FULL_NAME_STYLE = 11;                   // data10
28395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int ORGANIZATION_PHONETIC_NAME_STYLE = 11;  // data10
28405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        public static final int PHONETIC_NAME_STYLE = 12;               // data11
2841d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2842d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2843d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    /**
2844d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * Updates a raw contact display name based on data rows, e.g. structured name,
2845d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     * organization, email etc.
2846d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov     */
2847ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateRawContactDisplayName(SQLiteDatabase db, long rawContactId) {
2848bca1c8b44f99528fc123d5547723e44771e8e934Mike Lockwood        int bestDisplayNameSource = DisplayNameSources.UNDEFINED;
28495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        NameSplitter.Name bestName = null;
28505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String bestDisplayName = null;
28515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String bestPhoneticName = null;
28525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        int bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
2853d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
28541129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(rawContactId);
28551129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        Cursor c = db.rawQuery(DisplayNameQuery.RAW_SQL, mSelectionArgs1);
2856d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        try {
2857d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            while (c.moveToNext()) {
28581129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                int mimeType = c.getInt(DisplayNameQuery.MIMETYPE);
28595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                int source = getDisplayNameSource(mimeType);
28605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (source < bestDisplayNameSource || source == DisplayNameSources.UNDEFINED) {
28615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    continue;
28625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                }
28631129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
28645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (source == bestDisplayNameSource && c.getInt(DisplayNameQuery.IS_PRIMARY) == 0) {
28655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    continue;
2866d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
28671129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
28685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (mimeType == mMimeTypeIdStructuredName) {
28695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    NameSplitter.Name name;
28705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (bestName != null) {
28715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name = new NameSplitter.Name();
28725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    } else {
28735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name = mName;
28745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        name.clear();
28755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
28765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.prefix = c.getString(DisplayNameQuery.PREFIX);
28775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.givenNames = c.getString(DisplayNameQuery.GIVEN_NAME);
28785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.middleName = c.getString(DisplayNameQuery.MIDDLE_NAME);
28795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.familyName = c.getString(DisplayNameQuery.FAMILY_NAME);
28805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.suffix = c.getString(DisplayNameQuery.SUFFIX);
28815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.fullNameStyle = c.isNull(DisplayNameQuery.FULL_NAME_STYLE)
28825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            ? FullNameStyle.UNDEFINED
28835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            : c.getInt(DisplayNameQuery.FULL_NAME_STYLE);
28845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticFamilyName = c.getString(DisplayNameQuery.PHONETIC_FAMILY_NAME);
28855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticMiddleName = c.getString(DisplayNameQuery.PHONETIC_MIDDLE_NAME);
28865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticGivenName = c.getString(DisplayNameQuery.PHONETIC_GIVEN_NAME);
28875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    name.phoneticNameStyle = c.isNull(DisplayNameQuery.PHONETIC_NAME_STYLE)
28885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            ? PhoneticNameStyle.UNDEFINED
28895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            : c.getInt(DisplayNameQuery.PHONETIC_NAME_STYLE);
28905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (!name.isEmpty()) {
28915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayNameSource = source;
28925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestName = name;
28935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
28945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                } else if (mimeType == mMimeTypeIdOrganization) {
28955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    mCharArrayBuffer.sizeCopied = 0;
28965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.DATA1, mCharArrayBuffer);
28975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (mCharArrayBuffer.sizeCopied != 0) {
2898d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                        bestDisplayNameSource = source;
28991129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
29001129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
29015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticName = c.getString(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME);
29025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticNameStyle =
29035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                c.isNull(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME_STYLE)
29045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                    ? PhoneticNameStyle.UNDEFINED
29055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                    : c.getInt(DisplayNameQuery.ORGANIZATION_PHONETIC_NAME_STYLE);
29065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    } else {
29075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        c.copyStringToBuffer(DisplayNameQuery.TITLE, mCharArrayBuffer);
29085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        if (mCharArrayBuffer.sizeCopied != 0) {
29091129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayNameSource = source;
29101129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                            bestDisplayName = new String(mCharArrayBuffer.data, 0,
29111129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                                    mCharArrayBuffer.sizeCopied);
29125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            bestPhoneticName = null;
29135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
29141129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                        }
2915d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                    }
29165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                } else {
29175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    // Display name is at DATA1 in all other types.
29185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    // This is ensured in the constructor.
29195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    mCharArrayBuffer.sizeCopied = 0;
29215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    c.copyStringToBuffer(DisplayNameQuery.DATA1, mCharArrayBuffer);
29225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    if (mCharArrayBuffer.sizeCopied != 0) {
29235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayNameSource = source;
29245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestDisplayName = new String(mCharArrayBuffer.data, 0,
29255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                                mCharArrayBuffer.sizeCopied);
29265dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticName = null;
29275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
29285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    }
2929d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov                }
2930d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            }
2931d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
2932d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        } finally {
2933d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov            c.close();
2934d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        }
2935d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
29365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String displayNamePrimary;
29375dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String displayNameAlternative;
29385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String sortKeyPrimary = null;
29395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String sortKeyAlternative = null;
29405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        int displayNameStyle = FullNameStyle.UNDEFINED;
29415dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (bestDisplayNameSource == DisplayNameSources.STRUCTURED_NAME) {
29435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNameStyle = bestName.fullNameStyle;
29445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.CJK
29455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    || displayNameStyle == FullNameStyle.UNDEFINED) {
29465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.getAdjustedFullNameStyle(displayNameStyle);
29475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bestName.fullNameStyle = displayNameStyle;
29485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
29495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNamePrimary = mNameSplitter.join(bestName, true);
29515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNameAlternative = mNameSplitter.join(bestName, false);
29525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            bestPhoneticName = mNameSplitter.joinPhoneticName(bestName);
29545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            bestPhoneticNameStyle = bestName.phoneticNameStyle;
29555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
29565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            displayNamePrimary = displayNameAlternative = bestDisplayName;
29575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
29585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (bestPhoneticName != null) {
29605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyPrimary = sortKeyAlternative = bestPhoneticName;
29615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (bestPhoneticNameStyle == PhoneticNameStyle.UNDEFINED) {
29625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bestPhoneticNameStyle = mNameSplitter.guessPhoneticNameStyle(bestPhoneticName);
29635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
29645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
29655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            if (displayNameStyle == FullNameStyle.UNDEFINED) {
29665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.guessFullNameStyle(bestDisplayName);
29675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                if (displayNameStyle == FullNameStyle.UNDEFINED
29685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                        || displayNameStyle == FullNameStyle.CJK) {
29695dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    displayNameStyle = mNameSplitter.getAdjustedNameStyleBasedOnPhoneticNameStyle(
29705dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                            displayNameStyle, bestPhoneticNameStyle);
29715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                }
29725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameStyle = mNameSplitter.getAdjustedFullNameStyle(displayNameStyle);
29735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
2974ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao            if (displayNameStyle == FullNameStyle.CHINESE ||
2975ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                    displayNameStyle == FullNameStyle.CJK) {
29765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                sortKeyPrimary = sortKeyAlternative =
2977cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao                        ContactLocaleUtils.getIntance().getSortKey(
2978ee0e6b105832366143e4ddb30beb5bb0e5c81ec5Bai Tao                                displayNamePrimary, displayNameStyle);
29795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            }
29805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
29815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (sortKeyPrimary == null) {
29835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyPrimary = displayNamePrimary;
29845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            sortKeyAlternative = displayNameAlternative;
29855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
29865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
29875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        setDisplayName(rawContactId, bestDisplayNameSource, displayNamePrimary,
29885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                displayNameAlternative, bestPhoneticName, bestPhoneticNameStyle,
29895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                sortKeyPrimary, sortKeyAlternative);
2990d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    }
2991d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
29921129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    private int getDisplayNameSource(int mimeTypeId) {
29931129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        if (mimeTypeId == mMimeTypeIdStructuredName) {
29941129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.STRUCTURED_NAME;
29951129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdEmail) {
29961129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.EMAIL;
29971129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdPhone) {
29981129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.PHONE;
29991129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdOrganization) {
30001129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.ORGANIZATION;
30011129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else if (mimeTypeId == mMimeTypeIdNickname) {
30021129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.NICKNAME;
30031129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        } else {
30041129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            return DisplayNameSources.UNDEFINED;
30051129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov        }
30061129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov    }
30071129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov
30089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
300920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     * Delete data row by row so that fixing of primaries etc work correctly.
301020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov     */
3011f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int deleteData(String selection, String[] selectionArgs, boolean callerIsSyncAdapter) {
301220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = 0;
301320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3014de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3015de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
301614bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, selection, selectionArgs, null);
3017de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        try {
3018de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            while(c.moveToNext()) {
301914bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
302014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov                String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
3021a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                DataRowHandler rowHandler = getDataRowHandler(mimeType);
3022a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov                count += rowHandler.delete(mDb, c);
3023f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                if (!callerIsSyncAdapter) {
302488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                    setRawContactDirty(rawContactId);
302588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov                }
302620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
302720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
3028de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            c.close();
302920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
303020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
303120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        return count;
303220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
303320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
303488e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov    /**
303588e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     * Delete a data row provided that it is one of the allowed mime types.
303688e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov     */
303720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public int deleteData(long dataId, String[] allowedMimeTypes) {
3038f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
303988e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
304088e5f4d32aa9cd3af0ac9654de479f1b8113f712Dmitri Plotnikov        // so we don't need to worry about deleting data we don't have permission to read.
30414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(dataId);
30424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        Cursor c = query(Data.CONTENT_URI, DataDeleteQuery.COLUMNS, Data._ID + "=?",
30434da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1, null);
3044f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov
304520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
304620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!c.moveToFirst()) {
304720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                return 0;
304820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
304920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
305014bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            String mimeType = c.getString(DataDeleteQuery.MIMETYPE);
305120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            boolean valid = false;
305220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            for (int i = 0; i < allowedMimeTypes.length; i++) {
305320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                if (TextUtils.equals(mimeType, allowedMimeTypes[i])) {
305420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    valid = true;
305520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                    break;
305620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                }
305720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
305820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
305920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            if (!valid) {
30607a4550f2afb24b2112b6c937f416c6f46ece35f4Fred Quintana                throw new IllegalArgumentException("Data type mismatch: expected "
306120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                        + Lists.newArrayList(allowedMimeTypes));
306220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
306320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3064a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov            DataRowHandler rowHandler = getDataRowHandler(mimeType);
3065813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return rowHandler.delete(mDb, c);
306620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
306720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
306820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
306920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
307020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
307120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    /**
3072ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
3073ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
3074f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private long insertGroup(Uri uri, ContentValues values, boolean callerIsSyncAdapter) {
3075f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.clear();
3076f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.putAll(values);
3077f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
3078e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final Account account = resolveAccount(uri, mValues);
3079ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3080ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
3081f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String packageName = mValues.getAsString(Groups.RES_PACKAGE);
308267dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        if (packageName != null) {
3083f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
308467dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        }
3085f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        mValues.remove(Groups.RES_PACKAGE);
3086ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3087dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        final boolean isFavoritesGroup = mValues.getAsLong(Groups.FAVORITES) != null
3088dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                ? mValues.getAsLong(Groups.FAVORITES) != 0
3089dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                : false;
3090dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3091f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter) {
3092f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            mValues.put(Groups.DIRTY, 1);
309373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
309473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3095f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        long result = mDb.insert(Tables.GROUPS, Groups.TITLE, mValues);
3096ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3097dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (!callerIsSyncAdapter && isFavoritesGroup) {
3098dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // add all starred raw contacts to this group
3099dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String selection;
3100dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs;
3101dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (account == null) {
3102dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + " IS NULL AND "
3103dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + " IS NULL";
3104dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = null;
3105dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3106dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selection = RawContacts.ACCOUNT_NAME + "=? AND "
3107dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        + RawContacts.ACCOUNT_TYPE + "=?";
3108dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                selectionArgs = new String[]{account.name, account.type};
3109dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3110dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor c = mDb.query(Tables.RAW_CONTACTS,
3111dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[]{RawContacts._ID, RawContacts.STARRED},
3112dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    selection, selectionArgs, null, null, null);
3113892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            try {
3114892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                while (c.moveToNext()) {
3115892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (c.getLong(1) != 0) {
3116892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        final long rawContactId = c.getLong(0);
3117892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        insertDataGroupMembership(rawContactId, result);
3118892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        setRawContactDirty(rawContactId);
3119892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    }
3120dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3121892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov            } finally {
3122892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                c.close();
3123dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3124dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
3125dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3126f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (mValues.containsKey(Groups.GROUP_VISIBLE)) {
31271a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3128ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        }
3129ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey
3130ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        return result;
3131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
3132ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
31335aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private long insertSettings(Uri uri, ContentValues values) {
3134e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final long id = mDb.insert(Tables.SETTINGS, null, values);
31355aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey
31361a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
31371a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3138e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
31391a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey
3140e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return id;
3141e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3142e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3143ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
314482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov     * Inserts a status update.
31451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
314682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public long insertStatusUpdate(ContentValues values) {
314782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        final String handle = values.getAsString(StatusUpdates.IM_HANDLE);
31480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        final Integer protocol = values.getAsInteger(StatusUpdates.PROTOCOL);
31494dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String customProtocol = null;
31504dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
31510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        if (protocol != null && protocol == Im.PROTOCOL_CUSTOM) {
315282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            customProtocol = values.getAsString(StatusUpdates.CUSTOM_PROTOCOL);
31534dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            if (TextUtils.isEmpty(customProtocol)) {
31544dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                throw new IllegalArgumentException(
31554dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                        "CUSTOM_PROTOCOL is required when PROTOCOL=PROTOCOL_CUSTOM");
31564dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov            }
31571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
31581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3159dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long rawContactId = -1;
3160dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        long contactId = -1;
316182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Long dataId = values.getAsLong(StatusUpdates.DATA_ID);
3162f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.setLength(0);
31632526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov        mSelectionArgs.clear();
3164dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton        if (dataId != null) {
3165dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the contact info for the given data row.
3166dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
31672526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSb.append(Tables.DATA + "." + Data._ID + "=?");
31682526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            mSelectionArgs.add(String.valueOf(dataId));
31691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
3170dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // Lookup the data row to attach this presence update to
3171dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
31720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(handle) || protocol == null) {
31730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                throw new IllegalArgumentException("PROTOCOL and IM_HANDLE are required");
31740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
31750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3176dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            // TODO: generalize to allow other providers to match against email
3177dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            boolean matchEmail = Im.PROTOCOL_GOOGLE_TALK == protocol;
3178dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton
31792526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov            String mimeTypeIdIm = String.valueOf(mMimeTypeIdIm);
3180dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            if (matchEmail) {
31812526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                String mimeTypeIdEmail = String.valueOf(mMimeTypeIdEmail);
3182f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3183f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // The following hack forces SQLite to use the (mimetype_id,data1) index, otherwise
3184f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the "OR" conjunction confuses it and it switches to a full scan of
3185f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // the raw_contacts table.
3186f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov
3187f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // This code relies on the fact that Im.DATA and Email.DATA are in fact the same
3188f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov                // column - Data.DATA1
31892526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + " IN (?,?)" +
31902526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Data.DATA1 + "=?" +
31912526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND ((" + DataColumns.MIMETYPE_ID + "=? AND " + Im.PROTOCOL + "=?");
31922526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
31932526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
31942526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
31952526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
31962526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
3197dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
31982526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
31992526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3200dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
32012526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(") OR (" + DataColumns.MIMETYPE_ID + "=?))");
32022526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdEmail);
3203dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            } else {
32042526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(DataColumns.MIMETYPE_ID + "=?" +
32052526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.PROTOCOL + "=?" +
32062526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                        " AND " + Im.DATA + "=?");
32072526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(mimeTypeIdIm);
32082526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(String.valueOf(protocol));
32092526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(handle);
3210dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                if (customProtocol != null) {
32112526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.append(" AND " + Im.CUSTOM_PROTOCOL + "=?");
32122526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSelectionArgs.add(customProtocol);
3213dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton                }
3214dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
32151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
321682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.DATA_ID)) {
32172526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSb.append(" AND " + DataColumns.CONCRETE_ID + "=?");
32182526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                mSelectionArgs.add(values.getAsString(StatusUpdates.DATA_ID));
3219dea2b6389abf1ae0448ae047b4b0a9e423381d77Jeff Hamilton            }
322070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
3221f8b937f62fb80445bf59b2e504d765bcab746557Dmitri Plotnikov        mSb.append(" AND ").append(getContactsRestrictions());
322270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
32231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
32241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
3225de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
32262526f94e532ac973c846db0099f94b375a23be93Dmitri Plotnikov                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
32274394086494fe7909aaca70f56fb4bb08beebf303Dmitri Plotnikov                    Clauses.CONTACT_VISIBLE + " DESC, " + Data.RAW_CONTACT_ID);
32281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
322967dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
32305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
3231e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
32321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
32331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
32341f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
32351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
32361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
323731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
323831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
323931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
32401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
32411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
324282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.PRESENCE)) {
3243a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (customProtocol == null) {
3244a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // We cannot allow a null in the custom protocol field, because SQLite3 does not
3245a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                // properly enforce uniqueness of null values
3246a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                customProtocol = "";
3247a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
3248a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3249a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.clear();
325082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.DATA_ID, dataId);
3251a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.RAW_CONTACT_ID, rawContactId);
3252a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mValues.put(PresenceColumns.CONTACT_ID, contactId);
325382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PROTOCOL, protocol);
325482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
325582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.IM_HANDLE, handle);
325682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            if (values.containsKey(StatusUpdates.IM_ACCOUNT)) {
325782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                mValues.put(StatusUpdates.IM_ACCOUNT, values.getAsString(StatusUpdates.IM_ACCOUNT));
3258a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            }
325982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            mValues.put(StatusUpdates.PRESENCE,
326082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                    values.getAsString(StatusUpdates.PRESENCE));
3261aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori            mValues.put(StatusUpdates.CHAT_CAPABILITY,
3262aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                    values.getAsString(StatusUpdates.CHAT_CAPABILITY));
32631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3264a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            // Insert the presence update
3265a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mDb.replace(Tables.PRESENCE, null, mValues);
3266a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3267e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
32680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
326982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        if (values.containsKey(StatusUpdates.STATUS)) {
327082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String status = values.getAsString(StatusUpdates.STATUS);
32710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String resPackage = values.getAsString(StatusUpdates.STATUS_RES_PACKAGE);
32720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Integer labelResource = values.getAsInteger(StatusUpdates.STATUS_LABEL);
32730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
32740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            if (TextUtils.isEmpty(resPackage)
32750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && (labelResource == null || labelResource == 0)
32760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    && protocol != null) {
32770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                labelResource = Im.getProtocolLabelResource(protocol);
32780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            }
32790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
32800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            Long iconResource = values.getAsLong(StatusUpdates.STATUS_ICON);
32810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            // TODO compute the default icon based on the protocol
32820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3283a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            if (TextUtils.isEmpty(status)) {
3284a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.bindLong(1, dataId);
3285a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateDelete.execute();
328682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            } else if (values.containsKey(StatusUpdates.STATUS_TIMESTAMP)) {
328782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                long timestamp = values.getAsLong(StatusUpdates.STATUS_TIMESTAMP);
3288a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(1, dataId);
3289a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.bindLong(2, timestamp);
32905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindString(mStatusUpdateReplace, 3, status);
32915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindString(mStatusUpdateReplace, 4, resPackage);
32925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindLong(mStatusUpdateReplace, 5, iconResource);
32935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                bindLong(mStatusUpdateReplace, 6, labelResource);
3294a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                mStatusUpdateReplace.execute();
3295a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            } else {
3296a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3297a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                try {
3298a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.bindLong(1, dataId);
32995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateInsert, 2, status);
33005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateInsert, 3, resPackage);
33015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusUpdateInsert, 4, iconResource);
33025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusUpdateInsert, 5, labelResource);
3303a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateInsert.executeInsert();
3304a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                } catch (SQLiteConstraintException e) {
3305a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    // The row already exists - update it
33060a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    long timestamp = System.currentTimeMillis();
3307a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(1, timestamp);
33085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateAutoTimestamp, 2, status);
3309a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.bindLong(3, dataId);
33105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusUpdateAutoTimestamp, 4, status);
3311a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                    mStatusUpdateAutoTimestamp.execute();
33120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
33135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindString(mStatusAttributionUpdate, 1, resPackage);
33145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusAttributionUpdate, 2, iconResource);
33155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                    bindLong(mStatusAttributionUpdate, 3, labelResource);
33160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.bindLong(4, dataId);
33170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                    mStatusAttributionUpdate.execute();
3318a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                }
3319e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov            }
3320e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        }
3321bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
3322a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        if (contactId != -1) {
3323a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(1, contactId);
3324a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.bindLong(2, contactId);
3325a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov            mLastStatusUpdate.execute();
3326a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        }
3327a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
3328a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        return dataId;
33291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
33301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
33314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3332de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
3333bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3334b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "deleteInTransaction: " + uri);
3335b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3336b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3337f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3338f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
3339508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
3340508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
334135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3342b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs);
334335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3344b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID:
3345b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3346b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3347b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3348b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs);
3349b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3350cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            case CONTACTS: {
3351cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                // TODO
3352cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                return 0;
3353cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3354cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3355d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3356d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
3357dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
33586bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
33596bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
33609fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP: {
33612e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
33622e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
33632e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3364fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3365fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
33662e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
33672e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
33682e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3369dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                return deleteContact(contactId, callerIsSyncAdapter);
33702e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
33712e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
33729fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            case CONTACTS_LOOKUP_ID: {
33739fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                // lookup contact by id and lookup key to see if they still match the actual record
33749fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final List<String> pathSegments = uri.getPathSegments();
33759fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final String lookupKey = pathSegments.get(2);
33769fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
33779fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                setTablesAndProjectionMapForContacts(lookupQb, uri, null);
3378a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
33799fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                String[] args;
33809fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                if (selectionArgs == null) {
33819fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[2];
33829fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } else {
33839fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    args = new String[selectionArgs.length + 2];
33849fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
33859fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
33869fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                args[0] = String.valueOf(contactId);
338760de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                args[1] = Uri.encode(lookupKey);
33889fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                lookupQb.appendWhere(Contacts._ID + "=? AND " + Contacts.LOOKUP_KEY + "=?");
33899fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                final SQLiteDatabase db = mDbHelper.getReadableDatabase();
33909fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                Cursor c = query(db, lookupQb, null, selection, args, null, null, null);
33919fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                try {
33929fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    if (c.getCount() == 1) {
33939fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // contact was unmodified so go ahead and delete it
3394dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                        return deleteContact(contactId, callerIsSyncAdapter);
33959fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    } else {
33969fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // row was changed (e.g. the merging might have changed), we got multiple
33979fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        // rows or the supplied selection filtered the record out
33989fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                        return 0;
33999fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    }
34009fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                } finally {
34019fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                    c.close();
34029fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann                }
34039fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann            }
34049fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
34052971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case RAW_CONTACTS: {
34062971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
3407fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                Cursor c = mDb.query(Tables.RAW_CONTACTS,
3408fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        new String[]{RawContacts._ID, RawContacts.CONTACT_ID},
3409e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
34102971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
34112971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
34122971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                        final long rawContactId = c.getLong(0);
3413fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        long contactId = c.getLong(1);
3414fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        numDeletes += deleteRawContact(rawContactId, contactId,
3415fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                                callerIsSyncAdapter);
34162971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
34172971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
34182971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
34192971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
34202971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
34212971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
34222971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
34235ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
34242971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                final long rawContactId = ContentUris.parseId(uri);
3425fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                return deleteRawContact(rawContactId, mDbHelper.getContactId(rawContactId),
3426fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov                        callerIsSyncAdapter);
3427508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3428508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
342920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3430f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
3431944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                return deleteData(appendAccountToSelection(uri, selection), selectionArgs,
3432f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        callerIsSyncAdapter);
343320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
343420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
343548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
343648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
343748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
343848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3439508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
3440f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
34414da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                mSelectionArgs1[0] = String.valueOf(dataId);
34424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                return deleteData(Data._ID + "=?", mSelectionArgs1, callerIsSyncAdapter);
3443ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3444ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3445ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3446f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mSyncToNetwork |= !callerIsSyncAdapter;
34475aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                return deleteGroup(uri, ContentUris.parseId(uri), callerIsSyncAdapter);
34482971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            }
34492971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana
34502971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana            case GROUPS: {
34512971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                int numDeletes = 0;
34522971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups._ID},
3453e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                        appendAccountToSelection(uri, selection), selectionArgs, null, null, null);
34542971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                try {
34552971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    while (c.moveToNext()) {
34565aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        numDeletes += deleteGroup(uri, c.getLong(0), callerIsSyncAdapter);
34572971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    }
34582971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                } finally {
34592971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                    c.close();
34602971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                }
346181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (numDeletes > 0) {
3462f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
346381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
34642971716e6a68660721d45be97bf3bd2dfad1c5efFred Quintana                return numDeletes;
3465508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
3466508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
3467eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
346843880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3469e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                return deleteSettings(uri, appendAccountToSelection(uri, selection), selectionArgs);
3470eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3471eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
347282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
34730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                return deleteStatusUpdates(selection, selectionArgs);
34741f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
34751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
347681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
347781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
34783cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov                return mLegacyApiSupport.delete(uri, selection, selectionArgs);
347981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
3480508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
34814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
34824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
34831c8e40c18f92722b9bec6e8ce2e345a9828efa16Dmitri Plotnikov    public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) {
3484ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3485b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final long groupMembershipMimetypeId = mDbHelper
348694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
3487de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov        mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
348894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
348994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                + groupId, null);
349094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
349194021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        try {
3492f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            if (callerIsSyncAdapter) {
3493de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
349494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            } else {
349594021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.clear();
349694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana                mValues.put(Groups.DELETED, 1);
3497f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                mValues.put(Groups.DIRTY, 1);
3498de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                return mDb.update(Tables.GROUPS, mValues, Groups._ID + "=" + groupId, null);
349994021b213e4db367f60b30fcbfe9019e28571784Fred Quintana            }
350094021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        } finally {
35011a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
350294021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
350394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
350494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
35055aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
3506e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.delete(Tables.SETTINGS, selection, selectionArgs);
35071a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        mVisibleTouched = true;
3508e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3509e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3510e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3511dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int deleteContact(long contactId, boolean callerIsSyncAdapter) {
351296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(contactId);
3513cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = mDb.query(Tables.RAW_CONTACTS, new String[]{RawContacts._ID},
351496b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                RawContacts.CONTACT_ID + "=?", mSelectionArgs1,
351596b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                null, null, null);
3516cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        try {
3517cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            while (c.moveToNext()) {
3518cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                long rawContactId = c.getLong(0);
3519dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
3520cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
3521cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
3522cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            c.close();
3523cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
3524cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3525cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        return mDb.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
3526cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3527cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
3528fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov    public int deleteRawContact(long rawContactId, long contactId, boolean callerIsSyncAdapter) {
35293389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
3530f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (callerIsSyncAdapter) {
353114bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov            mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
3532fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            int count = mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
3533fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            mContactAggregator.updateDisplayNameForContact(mDb, contactId);
3534fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return count;
353533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } else {
3536b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mDbHelper.removeContactIfSingleton(rawContactId);
3537dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            return markRawContactAsDeleted(rawContactId, callerIsSyncAdapter);
353833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        }
353933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
354033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
35410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private int deleteStatusUpdates(String selection, String[] selectionArgs) {
35429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // delete from both tables: presence and status_updates
35439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      // TODO should account type/name be appended to the where clause?
35449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      if (VERBOSE_LOGGING) {
35459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          Log.v(TAG, "deleting data from status_updates for " + selection);
35469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      }
35479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      mDb.delete(Tables.STATUS_UPDATES, getWhereClauseForStatusUpdatesTable(selection),
35489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          selectionArgs);
35499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori      return mDb.delete(Tables.PRESENCE, selection, selectionArgs);
35500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
35510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
3552dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int markRawContactAsDeleted(long rawContactId, boolean callerIsSyncAdapter) {
355381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mSyncToNetwork = true;
355481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
3555cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.clear();
3556cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DELETED, 1);
3557cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
3558cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
3559cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.putNull(RawContacts.CONTACT_ID);
3560cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mValues.put(RawContacts.DIRTY, 1);
3561dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        return updateRawContact(rawContactId, mValues, callerIsSyncAdapter);
3562cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
3563cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
35644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
3565de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
3566de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov            String[] selectionArgs) {
3567bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
3568b5a4add17815167d20a90645779df34cdf45280dFred Quintana            Log.v(TAG, "updateInTransaction: " + uri);
3569b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3570b5a4add17815167d20a90645779df34cdf45280dFred Quintana
357135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
357200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
357300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
3574b5a4add17815167d20a90645779df34cdf45280dFred Quintana        if (match == SYNCSTATE_ID && selection == null) {
3575b5a4add17815167d20a90645779df34cdf45280dFred Quintana            long rowId = ContentUris.parseId(uri);
35761129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov            Object data = values.get(ContactsContract.SyncState.DATA);
3577b5a4add17815167d20a90645779df34cdf45280dFred Quintana            mUpdatedSyncStates.put(rowId, data);
3578b5a4add17815167d20a90645779df34cdf45280dFred Quintana            return 1;
3579b5a4add17815167d20a90645779df34cdf45280dFred Quintana        }
3580b5a4add17815167d20a90645779df34cdf45280dFred Quintana        flushTransactionalChanges();
3581f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        final boolean callerIsSyncAdapter =
3582f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false);
358300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
358435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
3585b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3586b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        appendAccountToSelection(uri, selection), selectionArgs);
3587b5a4add17815167d20a90645779df34cdf45280dFred Quintana
3588b5a4add17815167d20a90645779df34cdf45280dFred Quintana            case SYNCSTATE_ID: {
3589b5a4add17815167d20a90645779df34cdf45280dFred Quintana                selection = appendAccountToSelection(uri, selection);
3590b5a4add17815167d20a90645779df34cdf45280dFred Quintana                String selectionWithId =
3591b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ")
3592b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        + (selection == null ? "" : " AND (" + selection + ")");
3593b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().update(mDb, values,
3594b5a4add17815167d20a90645779df34cdf45280dFred Quintana                        selectionWithId, selectionArgs);
3595b5a4add17815167d20a90645779df34cdf45280dFred Quintana            }
359635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
3597d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
3598dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(values, selection, selectionArgs, callerIsSyncAdapter);
359900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
360000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
360100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3602d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
3603dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(ContentUris.parseId(uri), values, callerIsSyncAdapter);
3604c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
3605c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
3606c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
36072e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP:
36082e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            case CONTACTS_LOOKUP_ID: {
36092e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final List<String> pathSegments = uri.getPathSegments();
36102e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final int segmentCount = pathSegments.size();
36112e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                if (segmentCount < 3) {
3612fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
3613fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
36142e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                }
36152e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final String lookupKey = pathSegments.get(2);
36162e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                final long contactId = lookupContactIdByLookupKey(mDb, lookupKey);
3617dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateContactOptions(contactId, values, callerIsSyncAdapter);
36182e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey                break;
36192e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey            }
36202e990cc352353b2e4d83f1eeecff137b94b84266Jeff Sharkey
36217d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            case RAW_CONTACTS_DATA: {
36227d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                final String rawContactId = uri.getPathSegments().get(1);
36237d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
36247d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                    + (selection == null ? "" : " AND " + selection);
36257d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
36267d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
36277d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
36287d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                break;
36297d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh            }
36307d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
363120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            case DATA: {
3632944abb09aa47fc08db668be8909fc4045a681117Cynthia Wong                count = updateData(uri, values, appendAccountToSelection(uri, selection),
3633f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
363481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3635f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
363681d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
363720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                break;
363820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3639c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
364048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case DATA_ID:
364148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
364248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
364348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
3644f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
364581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3646f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
364781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
364800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
364900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
36507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
36515ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
36525ac5c70d1165309302ebcc931f51723e37d31e0bJeff Sharkey                selection = appendAccountToSelection(uri, selection);
3653dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                count = updateRawContacts(values, selection, selectionArgs, callerIsSyncAdapter);
36547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
36557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
36567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
36575ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
365833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
36594529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                if (selection != null) {
36604da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
36614da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    count = updateRawContacts(values, RawContacts._ID + "=?"
3662dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                                    + " AND(" + selection + ")", selectionArgs,
3663dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
36644529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                } else {
36654da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    mSelectionArgs1[0] = String.valueOf(rawContactId);
3666dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    count = updateRawContacts(values, RawContacts._ID + "=?", mSelectionArgs1,
3667dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            callerIsSyncAdapter);
36684529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                }
36697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
36707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
36717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
3672ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
36735aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, appendAccountToSelection(uri, selection),
3674f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                        selectionArgs, callerIsSyncAdapter);
367581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3676f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
367781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3678ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3679ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3680ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3681ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
3682ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
36834da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(groupId));
36844da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                String selectionWithId = Groups._ID + "=? "
368573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                        + (selection == null ? "" : " AND " + selection);
36865aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                count = updateGroups(uri, values, selectionWithId, selectionArgs,
36875aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey                        callerIsSyncAdapter);
368881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                if (count > 0) {
3689f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                    mSyncToNetwork |= !callerIsSyncAdapter;
369081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                }
3691ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
3692ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
3693ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3694127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
3695de955f25491cdc0e826ea5c7d4cd0e93cb970fb7Dmitri Plotnikov                count = updateAggregationException(mDb, values);
3696b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
3697b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
3698b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
3699eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
3700e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                count = updateSettings(uri, values, appendAccountToSelection(uri, selection),
3701e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                        selectionArgs);
370243880c9228332d0ea1426341fcf712d302b2c55bDmitri Plotnikov                mSyncToNetwork |= !callerIsSyncAdapter;
3703eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
3704eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
3705eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
37069705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            case STATUS_UPDATES: {
37079705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                count = updateStatusUpdate(uri, values, selection, selectionArgs);
37089705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                break;
37099705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            }
37109705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
371172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            case DIRECTORIES: {
371272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                mContactDirectoryManager.scheduleDirectoryUpdateForCaller();
371372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                count = 1;
3714d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
3715d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
3716d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
371781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            default: {
371881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                mSyncToNetwork = true;
3719f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
372081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            }
372100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
372200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
372300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
37244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
37254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
37269705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private int updateStatusUpdate(Uri uri, ContentValues values, String selection,
37279705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        String[] selectionArgs) {
37289705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // update status_updates table, if status is provided
37299705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO should account type/name be appended to the where clause?
37309705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        int updateCount = 0;
37319705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues settableValues = getSettableColumnsForStatusUpdatesTable(values);
37329705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
37339705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.STATUS_UPDATES,
37349705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    settableValues,
37359705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    getWhereClauseForStatusUpdatesTable(selection),
37369705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selectionArgs);
37379705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
37389705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37399705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // now update the Presence table
37409705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        settableValues = getSettableColumnsForPresenceTable(values);
37419705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        if (settableValues.size() > 0) {
37429705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori          updateCount = mDb.update(Tables.PRESENCE, settableValues,
37439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                    selection, selectionArgs);
37449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        }
37459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // TODO updateCount is not entirely a valid count of updated rows because 2 tables could
37469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // potentially get updated in this method.
37479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return updateCount;
37489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
37519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Build a where clause to select the rows to be updated in status_updates table.
37529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
37539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private String getWhereClauseForStatusUpdatesTable(String selection) {
37549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.setLength(0);
37559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE);
37569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(selection);
37579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mSb.append(")");
37589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mSb.toString();
37599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForStatusUpdatesTable(ContentValues values) {
37629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
37639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS, values,
37649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS);
37659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_TIMESTAMP, values,
37669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_TIMESTAMP);
37679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_RES_PACKAGE, values,
37689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_RES_PACKAGE);
37699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_LABEL, values,
37709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_LABEL);
37719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.STATUS_ICON, values,
37729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.STATUS_ICON);
37739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
37749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    private ContentValues getSettableColumnsForPresenceTable(ContentValues values) {
37779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        mValues.clear();
37789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.PRESENCE, values,
37799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori            StatusUpdates.PRESENCE);
3780aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori        ContactsDatabaseHelper.copyStringValue(mValues, StatusUpdates.CHAT_CAPABILITY, values,
3781aabcd1d34a71ad06ee0a9395331540484f1ceb17Vasu Nori                StatusUpdates.CHAT_CAPABILITY);
37829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        return mValues;
37839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
37849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
37855aec18c7ba70a011ffff949cfa3faaffce0a79c7Jeff Sharkey    private int updateGroups(Uri uri, ContentValues values, String selectionWithId,
3786f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
378773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3788ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        mGroupIdCache.clear();
3789ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov
379073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        ContentValues updatedValues;
3791f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana        if (!callerIsSyncAdapter && !values.containsKey(Groups.DIRTY)) {
379273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = mValues;
379373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.clear();
379473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.putAll(values);
379573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues.put(Groups.DIRTY, 1);
379673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } else {
379773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            updatedValues = values;
379873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
379973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
3800ac004680e3cc127b6ebf32b78d2813654b9c56fbJeff Sharkey        int count = mDb.update(Tables.GROUPS, updatedValues, selectionWithId, selectionArgs);
38011a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (updatedValues.containsKey(Groups.GROUP_VISIBLE)) {
38021a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
380394021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        }
38046ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        if (updatedValues.containsKey(Groups.SHOULD_SYNC)
38051129311abb3db9bf6eb5731da054276164e0b8d1Dmitri Plotnikov                && updatedValues.getAsInteger(Groups.SHOULD_SYNC) != 0) {
38066ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            Cursor c = mDb.query(Tables.GROUPS, new String[]{Groups.ACCOUNT_NAME,
3807e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                    Groups.ACCOUNT_TYPE}, selectionWithId, selectionArgs, null,
38086ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    null, null);
38096ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountName;
38106ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            String accountType;
38116ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            try {
38126ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                while (c.moveToNext()) {
38136ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountName = c.getString(0);
38146ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    accountType = c.getString(1);
38156ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    if(!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
38166ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        Account account = new Account(accountName, accountType);
3817ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                        ContentResolver.requestSync(account, ContactsContract.AUTHORITY,
38186ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                                new Bundle());
38196ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                        break;
38206ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                    }
38216ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                }
38226ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            } finally {
38236ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi                c.close();
38246ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi            }
38256ce24dc1d53f1ed2760a06cd60c705ebdf666f43Megha Joshi        }
382694021b213e4db367f60b30fcbfe9019e28571784Fred Quintana        return count;
382794021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
382894021b213e4db367f60b30fcbfe9019e28571784Fred Quintana
3829b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov    private int updateSettings(Uri uri, ContentValues values, String selection,
3830b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            String[] selectionArgs) {
3831e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs);
38321a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey        if (values.containsKey(Settings.UNGROUPED_VISIBLE)) {
38331a21fa6383449df4bf0d46138a23aa02dfa235a0Jeff Sharkey            mVisibleTouched = true;
3834e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        }
3835e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey        return count;
3836e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey    }
3837e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
3838dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContacts(ContentValues values, String selection, String[] selectionArgs,
3839dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
38404529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        if (values.containsKey(RawContacts.CONTACT_ID)) {
38414529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            throw new IllegalArgumentException(RawContacts.CONTACT_ID + " should not be included " +
38424529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                    "in content values. Contact IDs are assigned automatically");
38434529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
384473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
384597fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
384697fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
384797fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0");
384897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
384997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
38504529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        int count = 0;
3851b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
385251bf5ea9531b9da72caff607dbdf35fd6f61cbe2Jeff Sharkey                new String[] { RawContacts._ID }, selection,
38534529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                selectionArgs, null, null, null);
38544529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        try {
38554529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            while (cursor.moveToNext()) {
38564529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                long rawContactId = cursor.getLong(0);
3857dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateRawContact(rawContactId, values, callerIsSyncAdapter);
38584529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                count++;
38594529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            }
38604529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        } finally {
38614529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov            cursor.close();
38624529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        }
38634529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
38644529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov        return count;
38654529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov    }
38664529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov
3867dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateRawContact(long rawContactId, ContentValues values,
3868dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
386996b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        final String selection = RawContacts._ID + " = ?";
387096b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        mSelectionArgs1[0] = Long.toString(rawContactId);
387119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        final boolean requestUndoDelete = (values.containsKey(RawContacts.DELETED)
387219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                && values.getAsInteger(RawContacts.DELETED) == 0);
387319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        int previousDeleted = 0;
3874ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountType = null;
3875ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov        String accountName = null;
387619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        if (requestUndoDelete) {
387719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            Cursor cursor = mDb.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS, selection,
387896b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker                    mSelectionArgs1, null, null, null);
387919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            try {
388019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                if (cursor.moveToFirst()) {
388119cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    previousDeleted = cursor.getInt(RawContactsQuery.DELETED);
3882ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountType = cursor.getString(RawContactsQuery.ACCOUNT_TYPE);
3883ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                    accountName = cursor.getString(RawContactsQuery.ACCOUNT_NAME);
388419cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                }
388519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            } finally {
388619cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                cursor.close();
388719cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
388819cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
388919cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                    ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT);
389019cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka        }
3891f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
389296b7618a3996f2f356cb33553e76877d23a996f2Doug Zongker        int count = mDb.update(Tables.RAW_CONTACTS, values, selection, mSelectionArgs1);
38935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count != 0) {
3894f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            if (values.containsKey(RawContacts.AGGREGATION_MODE)) {
3895f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                int aggregationMode = values.getAsInteger(RawContacts.AGGREGATION_MODE);
3896f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov
3897f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // As per ContactsContract documentation, changing aggregation mode
3898f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                // to DEFAULT should not trigger aggregation
3899f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                if (aggregationMode != RawContacts.AGGREGATION_MODE_DEFAULT) {
390069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                    mContactAggregator.markForAggregation(rawContactId, aggregationMode, false);
3901f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov                }
3902f5a847e8c93db02f3334dbc276debd90bdea5658Dmitri Plotnikov            }
3903433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            if (values.containsKey(RawContacts.STARRED)) {
3904dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter) {
3905dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
3906dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            values.getAsLong(RawContacts.STARRED) != 0);
3907dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
39084529f06d48c8ad3cc573a9b7b8f2f952b1e20dcdDmitri Plotnikov                mContactAggregator.updateStarred(rawContactId);
3909dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } else {
3910dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // if this raw contact is being associated with an account, then update the
3911dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // favorites group membership based on whether or not this contact is starred.
3912dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // If it is starred, add a group membership, if one doesn't already exist
3913dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                // otherwise delete any matching group memberships.
3914dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3915dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    boolean starred = 0 != DatabaseUtils.longForQuery(mDb,
3916dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            SELECTION_STARRED_FROM_RAW_CONTACTS,
3917dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            new String[]{Long.toString(rawContactId)});
3918dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId, starred);
3919dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
3920dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
3921dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3922dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // if this raw contact is being associated with an account, then add a
3923dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            // group membership to the group marked as AutoAdd, if any.
3924dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            if (!callerIsSyncAdapter && values.containsKey(RawContacts.ACCOUNT_NAME)) {
3925dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                addAutoAddMembership(rawContactId);
3926433a3a08a4726952c080e22e3969ac6c4f28e8dfJeff Sharkey            }
3927dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
3928285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            if (values.containsKey(RawContacts.SOURCE_ID)) {
39292b7a632bba423357ae5641f94da6a2f71afc523bDmitri Plotnikov                mContactAggregator.updateLookupKeyForRawContact(mDb, rawContactId);
3930285b771bc955305fa6d49ca23f808cecc8a13d5eDmitri Plotnikov            }
3931f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            if (values.containsKey(RawContacts.NAME_VERIFIED)) {
3932f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov
3933f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // If setting NAME_VERIFIED for this raw contact, reset it for all
3934f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                // other raw contacts in the same aggregate
3935f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                if (values.getAsInteger(RawContacts.NAME_VERIFIED) != 0) {
3936f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.bindLong(1, rawContactId);
3937f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.bindLong(2, rawContactId);
3938f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                    mResetNameVerifiedForOtherRawContacts.execute();
3939f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                }
3940f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov                mContactAggregator.updateDisplayNameForRawContact(mDb, rawContactId);
3941f01c876a92b9c950a0450ed8b706ac5eb2c9b660Dmitri Plotnikov            }
394219cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            if (requestUndoDelete && previousDeleted == 1) {
394319cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka                // undo delete, needs aggregation again.
3944ab91a8babdbda516d4e5088fedf3fdebf9cf88adDmitri Plotnikov                mInsertedRawContacts.put(rawContactId, new Account(accountName, accountType));
394519cf97e1f8a569ad782756183419b7ba45ce15a0Tadashi G. Takaoka            }
39465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
39475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return count;
394833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov    }
394933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
3950321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    private int updateData(Uri uri, ContentValues values, String selection,
3951f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
395220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.clear();
395320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.putAll(values);
395420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data._ID);
39555ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mValues.remove(Data.RAW_CONTACT_ID);
395620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mValues.remove(Data.MIMETYPE);
395720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
395820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        String packageName = values.getAsString(Data.RES_PACKAGE);
395920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        if (packageName != null) {
396020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            mValues.remove(Data.RES_PACKAGE);
3961b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName));
396220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
396320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
396470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY);
396570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        boolean containsIsPrimary = mValues.containsKey(Data.IS_PRIMARY);
396620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
396720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Remove primary or super primary values being set to 0. This is disallowed by the
396820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // content provider.
396970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsSuperPrimary && mValues.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
397020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsSuperPrimary = false;
397170b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_SUPER_PRIMARY);
397220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
397370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        if (containsIsPrimary && mValues.getAsInteger(Data.IS_PRIMARY) == 0) {
397420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            containsIsPrimary = false;
397570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            mValues.remove(Data.IS_PRIMARY);
397620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
397720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
397897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        if (!callerIsSyncAdapter) {
397997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov            selection = DatabaseUtils.concatenateWhere(selection,
398097fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                    Data.IS_READ_ONLY + "=0");
398197fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        }
398297fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov
3983653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        int count = 0;
398420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3985653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // Note that the query will return data according to the access restrictions,
3986653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        // so we don't need to worry about updating data we don't have permission to read.
398714bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        Cursor c = query(uri, DataUpdateQuery.COLUMNS, selection, selectionArgs, null);
3988653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        try {
3989653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            while(c.moveToNext()) {
3990f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana                count += updateData(mValues, c, callerIsSyncAdapter);
399120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            }
3992653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        } finally {
3993653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            c.close();
399420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
399520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3996653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        return count;
399720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
399820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
3999f6be85f72615168c836b05c03ab5fc80d4794a82Fred Quintana    private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
4000653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        if (values.size() == 0) {
4001653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov            return 0;
4002321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana        }
4003653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
400414bba94bbe0f2e215ad7b3b9417754a1ba0d95bfDmitri Plotnikov        final String mimeType = c.getString(DataUpdateQuery.MIMETYPE);
4005a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        DataRowHandler rowHandler = getDataRowHandler(mimeType);
4006813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        if (rowHandler.update(mDb, values, c, callerIsSyncAdapter)) {
4007813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 1;
4008813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        } else {
4009813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return 0;
4010a6733943584294492aa0118fc32bf4e58dabb028Dmitri Plotnikov        }
4011321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana    }
4012321afd997c985f150a13e0a5538e2a12b755b217Fred Quintana
40138c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    private int updateContactOptions(ContentValues values, String selection,
4014dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            String[] selectionArgs, boolean callerIsSyncAdapter) {
40158c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        int count = 0;
4016b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        Cursor cursor = mDb.query(mDbHelper.getContactView(),
40178c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                new String[] { Contacts._ID }, selection,
40188c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                selectionArgs, null, null, null);
40198c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        try {
40208c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            while (cursor.moveToNext()) {
40218c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                long contactId = cursor.getLong(0);
4022dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                updateContactOptions(contactId, values, callerIsSyncAdapter);
40238c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                count++;
40248c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            }
40258c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        } finally {
40268c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            cursor.close();
40278c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        }
40288c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
40298c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        return count;
40308c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    }
40318c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4032dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private int updateContactOptions(long contactId, ContentValues values,
4033dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            boolean callerIsSyncAdapter) {
4034d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
40358c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4036b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
4037d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4038b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
4039d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4040b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
4041d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4042b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
4043d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4044b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
4045d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                values, Contacts.STARRED);
4046d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4047d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
40488c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.size() == 0) {
4049d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
4050d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
4051d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
40528c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        if (mValues.containsKey(RawContacts.STARRED)) {
4053c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey            // Mark dirty when changing starred to trigger sync
40548c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov            mValues.put(RawContacts.DIRTY, 1);
4055c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey        }
4056c76d0a78fe2d3471195cfa555bab016eec154f07Jeff Sharkey
40574da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov        mSelectionArgs1[0] = String.valueOf(contactId);
405897fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov        mDb.update(Tables.RAW_CONTACTS, mValues, RawContacts.CONTACT_ID + "=?"
405997fd30388bd6530f86679510cd7b43b9c518bcefDmitri Plotnikov                + " AND " + RawContacts.RAW_CONTACT_IS_READ_ONLY + "=0", mSelectionArgs1);
40608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
4061dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        if (mValues.containsKey(RawContacts.STARRED) && !callerIsSyncAdapter) {
4062dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            Cursor cursor = mDb.query(mDbHelper.getRawContactView(),
4063dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=?",
4064dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    mSelectionArgs1, null, null, null);
4065dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            try {
4066dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                while (cursor.moveToNext()) {
4067dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    long rawContactId = cursor.getLong(0);
4068dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                    updateFavoritesMembership(rawContactId,
4069dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                            mValues.getAsLong(RawContacts.STARRED) != 0);
4070dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                }
4071dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            } finally {
4072dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                cursor.close();
4073dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            }
4074dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana        }
4075dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana
40768c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // Copy changeable values to prevent automatically managed fields from
40778c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        // being explicitly updated by clients.
40788c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        mValues.clear();
4079b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
40808c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.CUSTOM_RINGTONE);
4081b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
40828c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.SEND_TO_VOICEMAIL);
4083b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
40848c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.LAST_TIME_CONTACTED);
4085b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
40868c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.TIMES_CONTACTED);
4087b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
40888c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                values, Contacts.STARRED);
40898c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
40909b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        int rslt = mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=?", mSelectionArgs1);
40916e38acbd1e72c62a6f8917297aed97e35c0c4697Vasu Nori
40929b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        if (values.containsKey(Contacts.LAST_TIME_CONTACTED) &&
40939b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori                !values.containsKey(Contacts.TIMES_CONTACTED)) {
40949b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
40959b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori            mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
40969b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        }
40979b1bd62417ef1764829398a61c3d5df93a924106Vasu Nori        return rslt;
4098f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
4099d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
4100127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
4101127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
41020c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId1 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID1);
41030c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rcId2 = values.getAsInteger(AggregationExceptions.RAW_CONTACT_ID2);
410480c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
41050c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1, rawContactId2;
41060c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (rcId1 < rcId2) {
41070c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId1;
41080c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId2;
41090c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
41100c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId2 = rcId1;
41110c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            rawContactId1 = rcId2;
4112b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
4113127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
41140c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
41154da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[0] = String.valueOf(rawContactId1);
41164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov            mSelectionArgs2[1] = String.valueOf(rawContactId2);
41170c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.delete(Tables.AGGREGATION_EXCEPTIONS,
41184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    AggregationExceptions.RAW_CONTACT_ID1 + "=? AND "
41194da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                    + AggregationExceptions.RAW_CONTACT_ID2 + "=?", mSelectionArgs2);
41200c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        } else {
41216bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            ContentValues exceptionValues = new ContentValues(3);
41226bc46c9f22aaa9e68f344b171426fc686d3b536aDmitri Plotnikov            exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
41230c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
41240c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            exceptionValues.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
41250c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov            db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
41260c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                    exceptionValues);
4127127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
4128127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
41293389f7c7df6c90e48fcb0c27832bc322e5b20bf6Dmitri Plotnikov        mContactAggregator.invalidateAggregationExceptionCache();
413069cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId1,
413169cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
413269cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov        mContactAggregator.markForAggregation(rawContactId2,
413369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                RawContacts.AGGREGATION_MODE_DEFAULT, true);
4134dea3ee5e7f84be2abfe35837a460cbe779d319dbDmitri Plotnikov
4135b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId1 = mDbHelper.getContactId(rawContactId1);
41360c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId1, contactId1);
41370c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
4138b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        long contactId2 = mDbHelper.getContactId(rawContactId2);
41390c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        mContactAggregator.aggregateContact(db, rawContactId2, contactId2);
4140127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
4141127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
4142127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
4143127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
4144b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
4145b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
414670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void onAccountsUpdated(Account[] accounts) {
4147f8536aaa7a52b9a7a353bc54e158becdbe79ec87Bai Tao        // TODO : Check the unit test.
4148e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov        boolean accountsChanged = false;
4149627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        HashSet<Account> existingAccounts = new HashSet<Account>();
415070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mDb.beginTransaction();
415170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        try {
4152dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana            findValidAccounts(existingAccounts);
4153743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov
4154743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // Add a row to the ACCOUNTS table for each new account
4155743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            for (Account account : accounts) {
4156743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                if (!existingAccounts.contains(account)) {
4157e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    accountsChanged = true;
4158743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                    mDb.execSQL("INSERT INTO " + Tables.ACCOUNTS + " (" + RawContacts.ACCOUNT_NAME
4159743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            + ", " + RawContacts.ACCOUNT_TYPE + ") VALUES (?, ?)",
4160743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                            new String[] {account.name, account.type});
4161743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                }
4162743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            }
416348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4164627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            // Remove all valid accounts from the existing account set. What is left
4165743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov            // in the accountsToDelete set will be extra accounts whose data must be deleted.
4166627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            HashSet<Account> accountsToDelete = new HashSet<Account>(existingAccounts);
4167627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (Account account : accounts) {
4168627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                accountsToDelete.remove(account);
416970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            }
417070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
417133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            if (!accountsToDelete.isEmpty()) {
4172e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                accountsChanged = true;
4173e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                for (Account account : accountsToDelete) {
4174e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    Log.d(TAG, "removing data for removed account " + account);
4175e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    String[] params = new String[] {account.name, account.type};
4176e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4177e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.GROUPS +
4178e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Groups.ACCOUNT_NAME + " = ?" +
4179e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + Groups.ACCOUNT_TYPE + " = ?", params);
4180e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4181e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.PRESENCE +
4182e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + PresenceColumns.RAW_CONTACT_ID + " IN (" +
4183e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    "SELECT " + RawContacts._ID +
4184e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " FROM " + Tables.RAW_CONTACTS +
4185e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4186e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                                    " AND " + RawContacts.ACCOUNT_TYPE + " = ?)", params);
4187e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4188e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.RAW_CONTACTS +
4189e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + " = ?" +
4190e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + " = ?", params);
4191e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4192e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.SETTINGS +
4193e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + Settings.ACCOUNT_NAME + " = ?" +
4194e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + Settings.ACCOUNT_TYPE + " = ?", params);
4195e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                    mDb.execSQL(
4196e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            "DELETE FROM " + Tables.ACCOUNTS +
4197e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " WHERE " + RawContacts.ACCOUNT_NAME + "=?" +
4198e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                            " AND " + RawContacts.ACCOUNT_TYPE + "=?", params);
4199d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDb.execSQL(
4200d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            "DELETE FROM " + Tables.DIRECTORIES +
4201d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " WHERE " + Directory.ACCOUNT_NAME + "=?" +
4202d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                            " AND " + Directory.ACCOUNT_TYPE + "=?", params);
42034458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    resetDirectoryCache();
4204e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                }
4205e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov
420633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // Find all aggregated contacts that used to contain the raw contacts
420733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                // we have just deleted and see if they are still referencing the deleted
4208e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                // names or photos.  If so, fix up those contacts.
420933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                HashSet<Long> orphanContactIds = Sets.newHashSet();
421033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                Cursor cursor = mDb.rawQuery("SELECT " + Contacts._ID +
421133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " FROM " + Tables.CONTACTS +
421233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " WHERE (" + Contacts.NAME_RAW_CONTACT_ID + " NOT NULL AND " +
421369cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                Contacts.NAME_RAW_CONTACT_ID + " NOT IN " +
421469cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + RawContacts._ID +
421569cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.RAW_CONTACTS + "))" +
421633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        " OR (" + Contacts.PHOTO_ID + " NOT NULL AND " +
421733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                                Contacts.PHOTO_ID + " NOT IN " +
421869cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        "(SELECT " + Data._ID +
421969cc3a2b09e2ffb606c6e52a71b604bba526d225Dmitri Plotnikov                                        " FROM " + Tables.DATA + "))", null);
422033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                try {
422133fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    while (cursor.moveToNext()) {
422233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                        orphanContactIds.add(cursor.getLong(0));
422333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    }
422433fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                } finally {
422533fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    cursor.close();
422633fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
422733fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
422833fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                for (Long contactId : orphanContactIds) {
422933fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                    mContactAggregator.updateAggregateData(contactId);
423033fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov                }
4231e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.updateAllVisible();
423233fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov            }
423333fd566fb6eebdd40a900c0c8a2f6dca894d7829Dmitri Plotnikov
4234e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            if (accountsChanged) {
4235e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov                mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
4236e3e79030101447da07547647bad225686eb9b8dfDmitri Plotnikov            }
423770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.setTransactionSuccessful();
423870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
423970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            mDb.endTransaction();
424070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
424173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.clear();
424270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
4243619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
424472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
424572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactDirectoryManager.onPackageChanged(packageName);
4246d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4247d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4248619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
4249627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     * Finds all distinct accounts present in the specified table.
4250627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov     */
4251dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana    private void findValidAccounts(Set<Account> validAccounts) {
4252743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov        Cursor c = mDb.rawQuery(
4253743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                "SELECT " + RawContacts.ACCOUNT_NAME + "," + RawContacts.ACCOUNT_TYPE +
4254743eac356404195f236ad44379fe9d180beb5bf2Dmitri Plotnikov                " FROM " + Tables.ACCOUNTS, null);
4255627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
4256627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            while (c.moveToNext()) {
4257dd5c25c65f09ada246c826fb6d04f0b6d4cf4388Fred Quintana                if (!c.isNull(0) || !c.isNull(1)) {
4258627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    validAccounts.add(new Account(c.getString(0), c.getString(1)));
4259627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
4260627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
4261627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } finally {
4262627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c.close();
4263627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
4264627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
4265627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
4266627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    /**
4267622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     * Test all against {@link TextUtils#isEmpty(CharSequence)}.
4268622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey     */
426967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    private static boolean areAllEmpty(ContentValues values, String[] keys) {
427067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        for (String key : keys) {
427167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            if (!TextUtils.isEmpty(values.getAsString(key))) {
427267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                return false;
427367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            }
427467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        }
427567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        return true;
427667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
427767c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
427867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    /**
427967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
428067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov     */
4281dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    private static boolean areAnySpecified(ContentValues values, String[] keys) {
4282622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        for (String key : keys) {
4283dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov            if (values.containsKey(key)) {
4284dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov                return true;
4285622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey            }
4286622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey        }
4287dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        return false;
4288622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey    }
4289622e0a2f00b3de248926ec9e89b11a6425919819Jeff Sharkey
42904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
42914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
42924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
4293d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
4294d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directory == null || directory.equals("0")) {
4295d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder, false);
4296d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        } else if (directory.equals("1")) {
4297d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            return queryLocal(uri, projection, selection, selectionArgs, sortOrder, true);
4298d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4299d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4300d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
4301d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo == null) {
4302d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            throw new IllegalArgumentException(
4303d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    mDbHelper.exceptionMessage("Invalid directory ID", uri));
4304d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4305d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4306d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Builder builder = new Uri.Builder();
4307d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.scheme(ContentResolver.SCHEME_CONTENT);
4308d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.authority(directoryInfo.authority);
4309d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        builder.encodedPath(uri.getEncodedPath());
4310d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountName != null) {
4311d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
4312d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
4313d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        if (directoryInfo.accountType != null) {
4314d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
4315d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        }
43162e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
43172e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limit = getLimit(uri);
43182e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        if (limit != null) {
43192e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov            builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
43202e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        }
43212e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov
4322d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        Uri directoryUri = builder.build();
432309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
432409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        if (projection == null) {
432509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            projection = getDefaultProjection(uri);
432609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
432709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
4328332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        Cursor cursor = getContext().getContentResolver().query(directoryUri, projection, selection,
4329d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs, sortOrder);
4330332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        while (cursor instanceof CursorWrapper) {
4331332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov            cursor = ((CursorWrapper)cursor).getWrappedCursor();
4332332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        }
4333332321f2832d52f50b9f8fc1f4006459000a4b21Dmitri Plotnikov        return cursor;
4334d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4335d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4336d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private static final class DirectoryQuery {
4337d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final String[] COLUMNS = new String[] {
4338d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory._ID,
4339d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
4340d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_NAME,
4341d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                Directory.ACCOUNT_TYPE
4342d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        };
4343d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4344d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int DIRECTORY_ID = 0;
4345d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int AUTHORITY = 1;
4346d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_NAME = 2;
4347d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov        public static final int ACCOUNT_TYPE = 3;
4348d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4349d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4350d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    /**
4351d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     * Reads and caches directory information for the database.
4352d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov     */
4353d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    private DirectoryInfo getDirectoryAuthority(String directoryId) {
43544458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized (mDirectoryCache) {
43554458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            if (!mDirectoryCacheValid) {
43564458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCache.clear();
43574458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                Cursor cursor = mDb.query(Tables.DIRECTORIES,
43584458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryQuery.COLUMNS,
43594458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        null, null, null, null, null);
43604458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                try {
43614458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    while (cursor.moveToNext()) {
43624458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        DirectoryInfo info = new DirectoryInfo();
43634458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        String id = cursor.getString(DirectoryQuery.DIRECTORY_ID);
43644458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.authority = cursor.getString(DirectoryQuery.AUTHORITY);
43654458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
43664458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
43674458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                        mDirectoryCache.put(id, info);
43684458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    }
43694458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                } finally {
43704458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                    cursor.close();
4371d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
43724458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov                mDirectoryCacheValid = true;
4373d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4374d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
43754458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            return mDirectoryCache.get(directoryId);
43764458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
4377d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    }
4378d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
437972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void resetDirectoryCache() {
43804458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        synchronized(mDirectoryCache) {
43814458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov            mDirectoryCacheValid = false;
43824458d63ef3384832fd2ad82130d4ad042cce2de6Dmitri Plotnikov        }
438372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
438472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
4385d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov    public Cursor queryLocal(Uri uri, String[] projection, String selection, String[] selectionArgs,
4386d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                String sortOrder, boolean hiddenOnly) {
4387bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        if (VERBOSE_LOGGING) {
4388bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov            Log.v(TAG, "query: " + uri);
4389bce6ee29f2d971ceae2bfce85a06bb3ecec6537aDmitri Plotnikov        }
43900b30cac29514b3978896731ba1df6a54fc94e9e4Dmitri Plotnikov
4391b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
439235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4393d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
43941f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
4395c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        String limit = getLimit(uri);
4396c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4397619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
4398619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
4399a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
44004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
440135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
4402b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
440335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
440435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4405d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS: {
4406763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4407d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                if (hiddenOnly) {
4408d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    qb.appendWhere(Contacts.IN_VISIBLE_GROUP + "=0");
4409d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
4410619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
4411619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
4412619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
4413d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov            case CONTACTS_ID: {
44144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = ContentUris.parseId(uri);
4415763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
44164da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
44174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
44186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
44196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
44206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
44215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP:
44225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            case CONTACTS_LOOKUP_ID: {
44235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
44245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int segmentCount = pathSegments.size();
44255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount < 3) {
4426fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4427fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                            "Missing a lookup key", uri));
44285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
4429a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
44305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String lookupKey = pathSegments.get(2);
44315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segmentCount == 4) {
44325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
44335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4434763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
4435a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4436a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4437a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4438a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
4439a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
44405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        return c;
44415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
44425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
44435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
4444763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
44454da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
44464da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
44474da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
44485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                break;
44495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
44505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
44512149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_DATA:
44522149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_DATA: {
44532149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
44542149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                int segmentCount = pathSegments.size();
44552149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount < 4) {
44562149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
44572149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                            "Missing a lookup key", uri));
44582149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
44592149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
44602149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                if (segmentCount == 5) {
44612149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
44622149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
44632149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
4464a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4465a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4466a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4467a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
4468a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
44692149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        return c;
44702149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    }
44712149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
44722149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                    // TODO see if the contact exists but has no data rows (rare)
44732149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                }
44742149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
44752149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
44762149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
44772149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
44782149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
44792149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov                break;
44802149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov            }
44812149ab82f021c204618d0d3644e261fd7a8d8490Dmitri Plotnikov
4482f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
4483f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                // When reading as vCard always use restricted view
448442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
4485763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                qb.setTables(mDbHelper.getContactView(true /* require restricted */));
4486f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                qb.setProjectionMap(sContactsVCardProjectionMap);
44874da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
44884da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
44894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Contacts._ID + "=?");
4490f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                break;
4491f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            }
4492f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey
449342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
449442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
449542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                String currentDateString = dateFormat.format(new Date()).toString();
449642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                return db.rawQuery(
449742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    "SELECT" +
449842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
449942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    " NULL AS " + OpenableColumns.SIZE,
450042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    new String[] { currentDateString });
450142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
450242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
4503ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_FILTER: {
4504916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                String filterParam = "";
4505ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
4506916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    filterParam = uri.getLastPathSegment();
4507ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
4508916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                setTablesAndProjectionMapForContactsWithSnippet(qb, uri, projection, filterParam);
4509d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                if (hiddenOnly) {
4510d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                    qb.appendWhere(Contacts.IN_VISIBLE_GROUP + "=0");
4511d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                }
4512ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4513ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4514ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
4515ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT_FILTER:
4516ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_STREQUENT: {
45174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                String filterSql = null;
4518ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                if (match == CONTACTS_STREQUENT_FILTER
4519d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
45204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
45214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4522e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov                    sb.append(Contacts._ID + " IN ");
45235e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    appendContactFilterAsNestedQuery(sb, filterParam);
45244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    filterSql = sb.toString();
45254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
45264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4527763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4528ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
45295e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] starredProjection = null;
45305e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                String[] frequentProjection = null;
45315e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                if (projection != null) {
4532dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    starredProjection =
4533dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
4534dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                    frequentProjection =
4535dd300fe5f5a1071b1c135af7c76e3ae149edda4dDmitri Plotnikov                            appendProjectionArg(projection, TIMES_CONTACTED_SORT_COLUMN);
45365e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                }
45375e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
45384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                // Build the first query for starred
45394a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
45404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4541d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
45425e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentStarredProjectionMap);
45435e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String starredQuery = qb.buildQuery(starredProjection, Contacts.STARRED + "=1",
45444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4545d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4546d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
4547d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
4548763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
45494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (filterSql != null) {
45504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    qb.appendWhere(filterSql);
4551d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
45525e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                qb.setProjectionMap(sStrequentFrequentProjectionMap);
45535e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar                final String frequentQuery = qb.buildQuery(frequentProjection,
4554d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        Contacts.TIMES_CONTACTED + " > 0 AND (" + Contacts.STARRED
4555d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                        + " = 0 OR " + Contacts.STARRED + " IS NULL)",
45564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                        null, Contacts._ID, null, null, null);
4557d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4558d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
4559d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
4560d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
45614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Cursor c = db.rawQuery(query, null);
45624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (c != null) {
4563d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
4564d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
4565d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
4566d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
4567d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
4568d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
4569ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov            case CONTACTS_GROUP: {
4570763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
4571b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
457271e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
45734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
4574b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
4575b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
4576b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
4577b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
4578a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_DATA: {
45794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
458082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
45824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
45836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
45846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
458500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
4586a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
45873653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
458882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
45894da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
45904da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
45913653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
45923653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov                break;
45933653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov            }
45943653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
4595a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_ENTITIES: {
4596a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
4597a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4598a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
4599a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
4600a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4601a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4602a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4603a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ENTITIES:
4604a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_LOOKUP_ID_ENTITIES: {
4605a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                List<String> pathSegments = uri.getPathSegments();
4606a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                int segmentCount = pathSegments.size();
4607a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount < 4) {
4608a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
4609a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            "Missing a lookup key", uri));
4610a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4611a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                String lookupKey = pathSegments.get(2);
4612a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                if (segmentCount == 5) {
4613a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    long contactId = Long.parseLong(pathSegments.get(3));
4614a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
4615a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
4616a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    lookupQb.appendWhere(" AND ");
4617a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4618a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
4619a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
4620a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.CONTACT_ID, contactId,
4621a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            Contacts.Entity.LOOKUP_KEY, lookupKey);
4622a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    if (c != null) {
4623a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        return c;
4624a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    }
4625a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                }
4626a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
4627a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForEntities(qb, uri, projection);
4628a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs,
4629a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
4630a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
4631a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                break;
4632a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            }
4633a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
46344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case PHONES: {
463582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
463689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
46372815f58f72f109790585931f601a63ddc02536a5Evan Millar                break;
46382815f58f72f109790585931f601a63ddc02536a5Evan Millar            }
46392815f58f72f109790585931f601a63ddc02536a5Evan Millar
464048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID: {
464182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
46424da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
464348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
46444da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
464548828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
464648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
464748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
4648ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
464982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
465089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
4651ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
46524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    String filterParam = uri.getLastPathSegment();
46534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    StringBuilder sb = new StringBuilder();
4654a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    sb.append(" AND (");
46555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
465645d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    boolean hasCondition = false;
46575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    boolean orNeeded = false;
46585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    String normalizedName = NameNormalizer.normalize(filterParam);
46595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    if (normalizedName.length() > 0) {
46605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data.RAW_CONTACT_ID + " IN ");
46617318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                        appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
46625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        orNeeded = true;
466345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
46645e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
46655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4666892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
4667892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    if (!TextUtils.isEmpty(number)) {
46685e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        if (orNeeded) {
46695e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                            sb.append(" OR ");
46705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        }
46715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                        sb.append(Data._ID +
4672892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
4673892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " FROM " + Tables.PHONE_LOOKUP
4674892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
4675892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append(number);
4676892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        sb.append("%')");
467745d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        hasCondition = true;
467845d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    }
467945d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov
468045d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                    if (!hasCondition) {
468145d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // If it is neither a phone number nor a name, the query should return
468245d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        // an empty cursor.  Let's ensure that.
468345d8626bf586b5c7111fa86324a7201ae8073607Dmitri Plotnikov                        sb.append("0");
46845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
46855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4686a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
4687ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
46885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
4689a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4690a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
4691a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
4692ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4693ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4694ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
46954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            case EMAILS: {
469682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
469789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
46984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                break;
46994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            }
47004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
470148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID: {
470282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
47034da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
47044da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
47054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        + " AND " + Data._ID + "=?");
470648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
470748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
470848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
47095e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_LOOKUP: {
471082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
471189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
47124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                if (uri.getPathSegments().size() > 2) {
471308768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String email = uri.getLastPathSegment();
471408768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    String address = mDbHelper.extractAddressFromEmailAddress(email);
471508768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    selectionArgs = insertSelectionArg(selectionArgs, address);
471608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
47174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                }
4718ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4719ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4720ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
47215e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            case EMAILS_FILTER: {
472282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, true);
472307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                String filterParam = null;
472407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
472507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    filterParam = uri.getLastPathSegment();
472607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    if (TextUtils.isEmpty(filterParam)) {
472707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                        filterParam = null;
472807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    }
472907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                }
47305e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
473107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                if (filterParam == null) {
473207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    // If the filter is unspecified, return nothing
473307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    qb.appendWhere(" AND 0");
473407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                } else {
473507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    StringBuilder sb = new StringBuilder();
473607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(" AND " + Data._ID + " IN (");
473707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    sb.append(
473807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            "SELECT " + Data._ID +
473907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " FROM " + Tables.DATA +
474007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " WHERE " + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
474107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            " AND " + Data.DATA1 + " LIKE ");
474207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
474320938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                    if (!filterParam.contains("@")) {
474420938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        String normalizedName = NameNormalizer.normalize(filterParam);
474520938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        if (normalizedName.length() > 0) {
474607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov
474707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            /*
474807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * Using a UNION instead of an "OR" to make SQLite use the right
474907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * indexes. We need it to use the (mimetype,data1) index for the
475007ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * email lookup (see above), but not for the name lookup.
475107ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * SQLite is not smart enough to use the index on one side of an OR
475207ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * but not on the other. Using two separate nested queries
475307ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             * and a UNION between them does the job.
475407ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                             */
475507ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                            sb.append(
475607ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " UNION SELECT " + Data._ID +
475707ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " FROM " + Tables.DATA +
475807ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " WHERE +" + DataColumns.MIMETYPE_ID + "=" + mMimeTypeIdEmail +
475907ce1624de652aa8494630a071b051a1670c4e3dDmitri Plotnikov                                    " AND " + Data.RAW_CONTACT_ID + " IN ");
47607318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov                            appendRawContactsByNormalizedNameFilter(sb, normalizedName, false);
476120938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov                        }
47625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    }
47635e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    sb.append(")");
4764a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov                    qb.appendWhere(sb);
47655e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                }
47665e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
4767a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                if (sortOrder == null) {
4768a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                    sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
4769a0e72d9b20207ec244f92ace2917932990f2bc8bDmitri Plotnikov                }
47705e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                break;
47715e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            }
47725e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
4773ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
477482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
477589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
477689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
4777ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
4778ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
4779ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
478048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID: {
478182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
47824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
478348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
478448828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
47854da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
478648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                break;
478748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            }
478848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov
47895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS: {
4790763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
47914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
47924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
47934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
47945ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_ID: {
47955ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = ContentUris.parseId(uri);
4796763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForRawContacts(qb, uri);
47974da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
47984da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
47994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
48004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
48014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
48025ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            case RAW_CONTACTS_DATA: {
48035ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
480482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
48054da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
48064da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
4807e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4808e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4809e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
4810e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
481182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
4812e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
4813e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
4814e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
48154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
481682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                setTablesAndProjectionMapForData(qb, uri, projection, false);
48174da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
48184da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + Data._ID + "=?");
4819a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                break;
4820a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov            }
4821a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov
4822a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
48234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4824a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
4825a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
4826a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
4827892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                    sortOrder = " length(lookup.normalized_number) DESC";
4828a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
4829a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4830e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
4831892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String numberE164 =
4832892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.formatNumberToE164(number, getCurrentCountryIso());
4833892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                String normalizedNumber =
4834892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                        PhoneNumberUtils.normalizeNumber(number);
4835892a3d9ded5c64a63ae3d5d5c52c59528b466c93Dmitri Plotnikov                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
4836e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                qb.setProjectionMap(sPhoneLookupProjectionMap);
4837e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                // Phone lookup cannot be combined with a selection
4838e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selection = null;
4839e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                selectionArgs = null;
4840a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
4841a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
4842a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4843ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
4844b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4845ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
484689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4847ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4848ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4849ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4850ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
4851b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView());
4852ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
48534da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
48544da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(Groups._ID + "=?");
4855ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4856ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4857ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4858ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
4859b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getGroupView() + " AS groups");
4860ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
486189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
486289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                groupBy = Groups._ID;
4863ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
4864ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
4865ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
4866b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
48670c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
4868b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
4869b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
4870b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
4871b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
487231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
4873d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                long contactId = Long.parseLong(uri.getPathSegments().get(1));
48742d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                String filter = null;
48752d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                if (uri.getPathSegments().size() > 3) {
48762d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                    filter = uri.getPathSegments().get(3);
48772d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov                }
487831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
4879d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                if (limit != null) {
4880d659078547c329b58f90d8809910a845d913dbc6Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(limit);
488131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
488231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
488331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
488431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
48855b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                ArrayList<AggregationSuggestionParameter> parameters = null;
48865b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                List<String> query = uri.getQueryParameters("query");
48875b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                if (query != null && !query.isEmpty()) {
48885b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
48895b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    for (String parameter : query) {
48905b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        int offset = parameter.indexOf(':');
48915b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        parameters.add(offset == -1
48925b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                ? new AggregationSuggestionParameter(
489376dfa406e2cde19c824983c37fc92c1c5bf63eecDaniel Lehmann                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
48945b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter)
48955b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                : new AggregationSuggestionParameter(
48965b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(0, offset),
48975b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                                        parameter.substring(offset + 1)));
48985b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                    }
48995b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                }
49005b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov
4901763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                setTablesAndProjectionMapForContacts(qb, uri, projection);
49027581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov
49037581213e160c460671aebdb054b8afd2f138d99eDmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
49045b3634b24d3c21618f96860e969fd5c9ba7d9ca8Dmitri Plotnikov                        maxSuggestions, filter, parameters);
490531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
490631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
4907eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            case SETTINGS: {
4908eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setTables(Tables.SETTINGS);
4909eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                qb.setProjectionMap(sSettingsProjectionMap);
491089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                appendAccountFromParameter(qb, uri);
4911e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4912e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // When requesting specific columns, this query requires
4913e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                // late-binding of the GroupMembership MIME-type.
4914b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                final String groupMembershipMimetypeId = Long.toString(mDbHelper
4915e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
491682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4917b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
4918e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4919e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
492082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                if (projection != null && projection.length != 0 &&
4921b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
4922e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
4923e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey                }
4924e07913c61c320b0cc2036db3b714e39534d8cd7aJeff Sharkey
4925eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey                break;
4926eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey            }
4927eb9ffdccfa19f5b2f22e688f733fd77e39605f9eJeff Sharkey
492882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES: {
49290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
49305ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
49315ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
49325ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
493382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            case STATUS_UPDATES_ID: {
49340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                setTableAndProjectionMapForStatusUpdates(qb, projection);
49354da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
49364da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
49375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                break;
49385ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            }
49395ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
4940c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS: {
4941a908fb5f39aa2021662a6cc317cc7e4db2d8bfb0Dmitri Plotnikov                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
4942c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4943c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
4944c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT: {
49452d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                String lookupKey = uri.getLastPathSegment();
49462d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, lookupKey, projection);
4947c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
4948c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
49491b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS:
4950b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
49511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
49521b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
49531b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
49541b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
4955b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
49561b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
49571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
49581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
49591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
49601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_FAVORITES:
4961b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
49621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
49631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.appendWhere(Contacts.STARRED + "=1");
49641b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
49651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
49661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
4967b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                qb.setTables(mDbHelper.getContactView());
49681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                qb.setProjectionMap(sLiveFoldersProjectionMap);
496971e051c79a57af70ec7b095074c3e7faf9507b52Dmitri Plotnikov                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
49701b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
49711b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov                break;
49721b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
497346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITIES: {
4974a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
497546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
497646b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
497746b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
497846b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            case RAW_CONTACT_ENTITY_ID: {
497946b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
4980a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                setTablesAndProjectionMapForRawEntities(qb, uri);
49814da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
49824da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                qb.appendWhere(" AND " + RawContacts._ID + "=?");
498346b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana                break;
498446b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana            }
498546b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
498609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            case PROVIDER_STATUS: {
498709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                return queryProviderStatus(uri, projection);
498809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
498909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
4990d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES : {
4991d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4992d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
4993d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
4994d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
4995d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
4996d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID : {
4997d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                long directoryId = ContentUris.parseId(uri);
4998d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setTables(Tables.DIRECTORIES);
4999d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.setProjectionMap(sDirectoryProjectionMap);
5000d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(directoryId));
5001d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                qb.appendWhere(Directory._ID + "=?");
5002d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                break;
5003d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            }
5004d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
50057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            case COMPLETE_NAME: {
50067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                return completeName(uri, projection);
50077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
50087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
50094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
5010f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
5011c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                        sortOrder, limit);
50124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
50134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
50147f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov        qb.setStrictProjectionMap(true);
50157f786e5cbde9975b9632beb9b6d19eeef8a64cf1Dmitri Plotnikov
5016ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        Cursor cursor =
5017ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
5018ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
5019ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
5020ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5021ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        return cursor;
50225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
50235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
50245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
50255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String selection, String[] selectionArgs, String sortOrder, String groupBy,
50265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            String limit) {
5027038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        if (projection != null && projection.length == 1
5028038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana                && BaseColumns._COUNT.equals(projection[0])) {
5029038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana            qb.setProjectionMap(sCountProjectionMap);
5030038c3db1b54dd9313c10c212025d37ca8a9e660fFred Quintana        }
50315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null,
50325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sortOrder, limit);
50334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
50344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
50354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
50364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
50374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
50384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
503909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    /**
504009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     * Creates a single-row cursor containing the current status of the provider.
504109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov     */
504209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    private Cursor queryProviderStatus(Uri uri, String[] projection) {
504309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
504409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        RowBuilder row = cursor.newRow();
504509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
504609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            if (ProviderStatus.STATUS.equals(projection[i])) {
504709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mProviderStatus);
504809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            } else if (ProviderStatus.DATA1.equals(projection[i])) {
504909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov                row.add(mEstimatedStorageRequirement);
505009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov            }
505109c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        }
505209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        return cursor;
505309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    }
505409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5055a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    /**
5056a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * Runs the query with the supplied contact ID and lookup ID.  If the query succeeds,
5057a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * it returns the resulting cursor, otherwise it returns null and the calling
5058a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     * method needs to resolve the lookup key and rerun the query.
5059a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov     */
5060a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private Cursor queryWithContactIdAndLookupKey(SQLiteQueryBuilder lookupQb,
5061a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            SQLiteDatabase db, Uri uri,
5062a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection, String selection, String[] selectionArgs,
5063a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String sortOrder, String groupBy, String limit,
5064a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn, long contactId, String lookupKeyColumn, String lookupKey) {
5065a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String[] args;
5066a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (selectionArgs == null) {
5067a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[2];
5068a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        } else {
5069a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            args = new String[selectionArgs.length + 2];
5070a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, args, 2, selectionArgs.length);
5071a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5072a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[0] = String.valueOf(contactId);
5073a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        args[1] = Uri.encode(lookupKey);
5074a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        lookupQb.appendWhere(contactIdColumn + "=? AND " + lookupKeyColumn + "=?");
5075a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        Cursor c = query(db, lookupQb, projection, selection, args, sortOrder,
5076a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                groupBy, limit);
5077a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (c.getCount() != 0) {
5078a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return c;
5079a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5080a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5081a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        c.close();
5082a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return null;
5083a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
508409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
5085bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov    private static final class AddressBookIndexQuery {
5086bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String LETTER = "letter";
5087bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String TITLE = "title";
5088bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String COUNT = "count";
5089ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5090bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final String[] COLUMNS = new String[] {
5091bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                LETTER, TITLE, COUNT
5092ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        };
5093ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5094bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_LETTER = 0;
5095bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_TITLE = 1;
5096bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        public static final int COLUMN_COUNT = 2;
5097bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5098de8f19d5cc1ef7d5bd76ede6be888dad37112966Daisuke Miyakawa        public static final String ORDER_BY = LETTER + " COLLATE " + PHONEBOOK_COLLATOR_NAME;
5099ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5100ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5101ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    /**
5102ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * Computes counts by the address book index titles and adds the resulting tally
5103ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     * to the returned cursor as a bundle of extras.
5104ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov     */
5105ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    private Cursor bundleLetterCountExtras(Cursor cursor, final SQLiteDatabase db,
5106ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            SQLiteQueryBuilder qb, String selection, String[] selectionArgs, String sortOrder) {
5107ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortKey;
5108ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5109ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // The sort order suffix could be something like "DESC".
5110ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // We want to preserve it in the query even though we will change
5111ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // the sort column itself.
5112ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        String sortOrderSuffix = "";
5113ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        if (sortOrder != null) {
5114ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int spaceIndex = sortOrder.indexOf(' ');
5115ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            if (spaceIndex != -1) {
5116ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder.substring(0, spaceIndex);
5117ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortOrderSuffix = sortOrder.substring(spaceIndex);
5118ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            } else {
5119ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                sortKey = sortOrder;
5120ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5121ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } else {
5122ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            sortKey = Contacts.SORT_KEY_PRIMARY;
5123ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5124ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5125bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        String locale = getLocale().toString();
5126ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        HashMap<String, String> projectionMap = Maps.newHashMap();
5127bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.LETTER,
5128bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "SUBSTR(" + sortKey + ",1,1) AS " + AddressBookIndexQuery.LETTER);
5129bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5130bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov        /**
5131bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * Use the GET_PHONEBOOK_INDEX function, which is an android extension for SQLite3,
5132bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * to map the first letter of the sort key to a character that is traditionally
5133bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * used in phonebooks to represent that letter.  For example, in Korean it will
5134bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * be the first consonant in the letter; for Japanese it will be Hiragana rather
5135bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         * than Katakana.
5136bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov         */
5137ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.TITLE,
5138bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                "GET_PHONEBOOK_INDEX(SUBSTR(" + sortKey + ",1,1),'" + locale + "')"
5139bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                        + " AS " + AddressBookIndexQuery.TITLE);
5140ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        projectionMap.put(AddressBookIndexQuery.COUNT,
5141ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                "COUNT(" + Contacts._ID + ") AS " + AddressBookIndexQuery.COUNT);
5142ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        qb.setProjectionMap(projectionMap);
5143ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5144f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov        Cursor indexCursor = qb.query(db, AddressBookIndexQuery.COLUMNS, selection, selectionArgs,
5145ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY, null /* having */,
5146ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                AddressBookIndexQuery.ORDER_BY + sortOrderSuffix);
5147ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5148ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        try {
5149f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            int groupCount = indexCursor.getCount();
5150ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            String titles[] = new String[groupCount];
5151ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            int counts[] = new int[groupCount];
5152bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            int indexCount = 0;
5153bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            String currentTitle = null;
5154bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5155bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // Since GET_PHONEBOOK_INDEX is a many-to-1 function, we may end up
5156bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // with multiple entries for the same title.  The following code
5157bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            // collapses those duplicates.
5158ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            for (int i = 0; i < groupCount; i++) {
5159f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov                indexCursor.moveToNext();
5160bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String title = indexCursor.getString(AddressBookIndexQuery.COLUMN_TITLE);
5161bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int count = indexCursor.getInt(AddressBookIndexQuery.COLUMN_COUNT);
5162bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                if (indexCount == 0 || !TextUtils.equals(title, currentTitle)) {
5163bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    titles[indexCount] = currentTitle = title;
5164bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount] = count;
5165bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    indexCount++;
5166bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                } else {
5167bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                    counts[indexCount - 1] += count;
5168bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                }
5169bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            }
5170bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5171bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov            if (indexCount < groupCount) {
5172bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                String[] newTitles = new String[indexCount];
5173bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(titles, 0, newTitles, 0, indexCount);
5174bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                titles = newTitles;
5175bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov
5176bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                int[] newCounts = new int[indexCount];
5177bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                System.arraycopy(counts, 0, newCounts, 0, indexCount);
5178bfd59cb11248659d12a379394774da8ff6f36cefDmitri Plotnikov                counts = newCounts;
5179ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            }
5180ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5181ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            final Bundle bundle = new Bundle();
5182ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            bundle.putStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, titles);
5183f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            bundle.putIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, counts);
5184ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            return new CursorWrapper(cursor) {
5185ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
5186ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                @Override
5187ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                public Bundle getExtras() {
5188ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                    return bundle;
5189ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                }
5190ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov            };
5191ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        } finally {
5192f3f4a385d8d1d6788ba79ca353d02235de1d9b33Dmitri Plotnikov            indexCursor.close();
5193ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        }
5194ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov    }
5195ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
51962d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    /**
519792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Returns the contact Id for the contact identified by the lookupKey.
519892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * Robust against changes in the lookup key: if the key has changed, will
519992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * look up the contact by the raw contact IDs or name encoded in the lookup
520092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov     * key.
52012d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill     */
52022d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill    public long lookupContactIdByLookupKey(SQLiteDatabase db, String lookupKey) {
52035870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContactLookupKey key = new ContactLookupKey();
52045870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ArrayList<LookupKeySegment> segments = key.parse(lookupKey);
52055870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
520692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        long contactId = -1;
520792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_SOURCE_ID)) {
520892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdBySourceIds(db, segments);
520992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
521092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
521192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
521292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
521392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
521492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        boolean hasRawContactIds =
521592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID);
521692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds) {
521792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            contactId = lookupContactIdByRawContactIds(db, segments);
521892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (contactId != -1) {
521992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return contactId;
522092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
522192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
522292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
522392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        if (hasRawContactIds
522492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                || lookupKeyContainsType(segments, ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME)) {
52255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            contactId = lookupContactIdByDisplayNames(db, segments);
52265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return contactId;
52295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
52305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private interface LookupBySourceIdQuery {
52325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
52335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
52355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
52365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
52375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
52385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.SOURCE_ID
52395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
52405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
52425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
52435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
52445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int SOURCE_ID = 3;
52455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
52465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long lookupContactIdBySourceIds(SQLiteDatabase db,
52485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
52495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
52505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(RawContacts.SOURCE_ID + " IN (");
52515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
52525870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
525392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID) {
52545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
52555870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
52565870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
52575870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52585870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
52595870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
52605870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupBySourceIdQuery.TABLE, LookupBySourceIdQuery.COLUMNS,
52625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
52635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
52645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
52655870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupBySourceIdQuery.ACCOUNT_TYPE);
52665870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupBySourceIdQuery.ACCOUNT_NAME);
52675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
52685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
52695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String sourceId = c.getString(LookupBySourceIdQuery.SOURCE_ID);
52705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
52715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
527292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_SOURCE_ID
527392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
52745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(sourceId)) {
52755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupBySourceIdQuery.CONTACT_ID);
52765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
52775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
52785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
52795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
52805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
52815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
52825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
52835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
52855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
52865870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
528792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByRawContactIdQuery {
528892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.RAW_CONTACTS;
52895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        String COLUMNS[] = {
52915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.CONTACT_ID,
52925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
52935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
529492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts._ID,
52955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        };
52965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
52975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int CONTACT_ID = 0;
52985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_TYPE = 1;
52995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int ACCOUNT_NAME = 2;
530092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ID = 3;
53015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
53025870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
530392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByRawContactIds(SQLiteDatabase db,
530492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            ArrayList<LookupKeySegment> segments) {
530592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
530692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(RawContacts._ID + " IN (");
53075870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
53085870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
530992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
531092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(segment.rawContactId);
531192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                sb.append(",");
53125870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
53135870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
531492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
531592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        sb.append(") AND " + RawContacts.CONTACT_ID + " NOT NULL");
53165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
531792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        Cursor c = db.query(LookupByRawContactIdQuery.TABLE, LookupByRawContactIdQuery.COLUMNS,
531892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                 sb.toString(), null, null, null, null);
531992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        try {
532092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            while (c.moveToNext()) {
532192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountType = c.getString(LookupByRawContactIdQuery.ACCOUNT_TYPE);
532292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String accountName = c.getString(LookupByRawContactIdQuery.ACCOUNT_NAME);
532392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                int accountHashCode =
532492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
532592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                String rawContactId = c.getString(LookupByRawContactIdQuery.ID);
532692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
532792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
532892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID
532992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
533092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && segment.rawContactId.equals(rawContactId)) {
533192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        segment.contactId = c.getLong(LookupByRawContactIdQuery.CONTACT_ID);
533292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                        break;
533392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    }
533492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                }
533592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
533692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        } finally {
533792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            c.close();
53385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
534092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return getMostReferencedContactId(segments);
534192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
534292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
534392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private interface LookupByDisplayNameQuery {
534492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
534592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
534692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        String COLUMNS[] = {
534792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.CONTACT_ID,
534892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
534992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
535092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME
535192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        };
535292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
535392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int CONTACT_ID = 0;
535492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_TYPE = 1;
535592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int ACCOUNT_NAME = 2;
535692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        int NORMALIZED_NAME = 3;
535792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
535892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
535992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private long lookupContactIdByDisplayNames(SQLiteDatabase db,
536092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                ArrayList<LookupKeySegment> segments) {
53615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
53625870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(NameLookupColumns.NORMALIZED_NAME + " IN (");
53635870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
53645870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
536592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
536692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID) {
53675870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                DatabaseUtils.appendEscapedSQLString(sb, segment.key);
53685870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                sb.append(",");
53695870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
53705870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53715870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.setLength(sb.length() - 1);      // Last comma
53725870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        sb.append(") AND " + NameLookupColumns.NAME_TYPE + "=" + NameLookupType.NAME_COLLATION_KEY
53735870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                + " AND " + RawContacts.CONTACT_ID + " NOT NULL");
53745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53755870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Cursor c = db.query(LookupByDisplayNameQuery.TABLE, LookupByDisplayNameQuery.COLUMNS,
53765870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                 sb.toString(), null, null, null, null);
53775870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        try {
53785870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            while (c.moveToNext()) {
53795870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountType = c.getString(LookupByDisplayNameQuery.ACCOUNT_TYPE);
53805870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String accountName = c.getString(LookupByDisplayNameQuery.ACCOUNT_NAME);
53815870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                int accountHashCode =
53825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        ContactLookupKey.getAccountHashCode(accountType, accountName);
53835870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                String name = c.getString(LookupByDisplayNameQuery.NORMALIZED_NAME);
53845870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                for (int i = 0; i < segments.size(); i++) {
53855870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    LookupKeySegment segment = segments.get(i);
538692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                    if ((segment.lookupType == ContactLookupKey.LOOKUP_TYPE_DISPLAY_NAME
538792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            || segment.lookupType == ContactLookupKey.LOOKUP_TYPE_RAW_CONTACT_ID)
538892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                            && accountHashCode == segment.accountHashCode
53895870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                            && segment.key.equals(name)) {
53905870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        segment.contactId = c.getLong(LookupByDisplayNameQuery.CONTACT_ID);
53915870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        break;
53925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
53935870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
53945870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
53955870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } finally {
53965870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c.close();
53975870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
53985870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
53995870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        return getMostReferencedContactId(segments);
54005870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
54015870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
540292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    private boolean lookupKeyContainsType(ArrayList<LookupKeySegment> segments, int lookupType) {
540392fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        for (int i = 0; i < segments.size(); i++) {
540492fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            LookupKeySegment segment = segments.get(i);
540592fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            if (segment.lookupType == lookupType) {
540692fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov                return true;
540792fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov            }
540892fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        }
540992fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
541092fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov        return false;
541192fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov    }
541292fcdfb24194e8527ef59c0af0731825ee46fa45Dmitri Plotnikov
5413ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    public void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
5414ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov        mContactAggregator.updateLookupKeyForRawContact(db, rawContactId);
5415ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov    }
5416ae7733451f6ddf3246efcd7fd4fc6882eefa6657Dmitri Plotnikov
54175870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    /**
54185870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     * Returns the contact ID that is mentioned the highest number of times.
54195870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov     */
54205870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    private long getMostReferencedContactId(ArrayList<LookupKeySegment> segments) {
54215870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        Collections.sort(segments);
54225870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
54235870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long bestContactId = -1;
54245870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int bestRefCount = 0;
54255870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
54265870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        long contactId = -1;
54275870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int count = 0;
54285870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
54295870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        int segmentCount = segments.size();
54305870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        for (int i = 0; i < segmentCount; i++) {
54315870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            LookupKeySegment segment = segments.get(i);
54325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            if (segment.contactId != -1) {
54335870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                if (segment.contactId == contactId) {
54345870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count++;
54355870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                } else {
54365870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    if (count > bestRefCount) {
54375870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestContactId = contactId;
54385870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                        bestRefCount = count;
54395870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    }
54405870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    contactId = segment.contactId;
54415870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    count = 1;
54425870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                }
54435870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            }
54445870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
54455870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        if (count > bestRefCount) {
54465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return contactId;
54475870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        } else {
54485870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            return bestContactId;
54495870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        }
54505870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov    }
54515870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
5452763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, Uri uri,
5453763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar            String[] projection) {
545482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5455916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5456916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5457916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionMap);
5458916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5459916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5460916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    /**
5461916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * Finds name lookup records matching the supplied filter, picks one arbitrary match per
5462916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     * contact and joins that with other contacts tables.
5463916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov     */
5464916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void setTablesAndProjectionMapForContactsWithSnippet(SQLiteQueryBuilder qb, Uri uri,
5465916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            String[] projection, String filter) {
5466916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5467916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5468916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        appendContactsTables(sb, uri, projection);
5469916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5470916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append(" JOIN (SELECT " +
5471916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                RawContacts.CONTACT_ID + " AS snippet_contact_id");
5472916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5473916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA_ID)) {
5474916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", " + DataColumns.CONCRETE_ID + " AS "
5475916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    + SearchSnippetColumns.SNIPPET_DATA_ID);
5476916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5477916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
54789c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA1)) {
54799c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA1 + " AS " + SearchSnippetColumns.SNIPPET_DATA1);
5480916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5481916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
54829c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA2)) {
54839c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA2 + " AS " + SearchSnippetColumns.SNIPPET_DATA2);
5484916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5485916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
54869c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA3)) {
54879c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA3 + " AS " + SearchSnippetColumns.SNIPPET_DATA3);
54889c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        }
54899c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov
54909c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_DATA4)) {
54919c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov            sb.append(", " + Data.DATA4 + " AS " + SearchSnippetColumns.SNIPPET_DATA4);
5492916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5493916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5494916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, SearchSnippetColumns.SNIPPET_MIMETYPE)) {
5495916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov            sb.append(", (" +
5496916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    "SELECT " + MimetypesColumns.MIMETYPE +
5497916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " FROM " + Tables.MIMETYPES +
5498916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " WHERE " + MimetypesColumns._ID + "=" + DataColumns.MIMETYPE_ID +
5499916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    ") AS " + SearchSnippetColumns.SNIPPET_MIMETYPE);
5500916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        }
5501916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5502c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        sb.append(" FROM " + Tables.DATA_JOIN_RAW_CONTACTS + " WHERE ");
5503c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
5504c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        if (!TextUtils.isEmpty(filter)) {
5505c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append(DataColumns.CONCRETE_ID + " IN (");
5506c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
5507c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            // Construct a query that gives us exactly one data _id per matching contact.
5508c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            // MIN stands in for ANY in this context.
5509c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append(
5510c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    "SELECT MIN(" + Tables.NAME_LOOKUP + "." + NameLookupColumns.DATA_ID + ")" +
5511c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    " FROM " + Tables.NAME_LOOKUP +
5512c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    " JOIN " + Tables.RAW_CONTACTS +
5513c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    " ON (" + RawContactsColumns.CONCRETE_ID
5514c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                            + "=" + Tables.NAME_LOOKUP + "." + NameLookupColumns.RAW_CONTACT_ID + ")" +
5515c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    " WHERE " + NameLookupColumns.NORMALIZED_NAME + " GLOB '");
5516c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append(NameNormalizer.normalize(filter));
5517c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
5518c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                        " IN(" + CONTACT_LOOKUP_NAME_TYPES + ")" +
5519c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    " GROUP BY " + RawContactsColumns.CONCRETE_CONTACT_ID +
5520c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov                    ")");
5521c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        } else {
5522c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov            sb.append("0");     // Empty filter - return an empty set
5523c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        }
5524c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov
5525c9e6d75562621a3dee26a99c2b082e2fd9b0c8b3Dmitri Plotnikov        sb.append(") ON (" + Contacts._ID + "=snippet_contact_id)");
5526916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5527916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setTables(sb.toString());
5528916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        qb.setProjectionMap(sContactsProjectionWithSnippetMap);
5529916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
5530916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
5531916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    private void appendContactsTables(StringBuilder sb, Uri uri, String[] projection) {
5532763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5533f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5534763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5535763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5536d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5537763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5538763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getContactView(excludeRestrictedData));
5539a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts._ID);
5540a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
554182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
5542ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
5543763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
5544763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        StringBuilder sb = new StringBuilder();
5545763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        boolean excludeRestrictedData = false;
5546f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5547763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5548763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        if (requestingPackage != null) {
5549d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton            excludeRestrictedData = !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5550763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        }
5551763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        sb.append(mDbHelper.getRawContactView(excludeRestrictedData));
5552763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setTables(sb.toString());
5553763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        qb.setProjectionMap(sRawContactsProjectionMap);
5554763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar        appendAccountFromParameter(qb, uri);
5555763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar    }
5556763100dcfabb368e72f25d24fe181c352bdb66d6Evan Millar
5557a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForRawEntities(SQLiteQueryBuilder qb, Uri uri) {
5558a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(mDbHelper.getRawEntitiesView(shouldExcludeRestrictedData(uri)));
5559a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sRawEntityProjectionMap);
556046b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana        appendAccountFromParameter(qb, uri);
556146b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana    }
556246b7bfa3728bf878d1a9dac9fea35fa629975e1bFred Quintana
556382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
556482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            String[] projection, boolean distinct) {
556582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5566a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getDataView(shouldExcludeRestrictedData(uri)));
556782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sb.append(" data");
556882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
5569a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
5570a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5571a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5572a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
55733296d3469bce0041a6cefc44d0486a2a7d0c9f82Jeff Sharkey
557482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setTables(sb.toString());
557582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        qb.setProjectionMap(distinct ? sDistinctDataProjectionMap : sDataProjectionMap);
557682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        appendAccountFromParameter(qb, uri);
5577ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov    }
5578ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
55790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb,
55800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            String[] projection) {
55810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        StringBuilder sb = new StringBuilder();
5582b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        sb.append(mDbHelper.getDataView());
55830a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        sb.append(" data");
5584a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
5585a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
55860a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5587a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5588a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sStatusUpdatesProjectionMap);
5589a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5590a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5591a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void setTablesAndProjectionMapForEntities(SQLiteQueryBuilder qb, Uri uri,
5592a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String[] projection) {
5593a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        StringBuilder sb = new StringBuilder();
5594a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(mDbHelper.getEntitiesView(shouldExcludeRestrictedData(uri)));
5595a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        sb.append(" data");
5596a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5597a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactPresenceJoin(sb, projection, Contacts.Entity.CONTACT_ID);
5598a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
5599a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataPresenceJoin(sb, projection, Contacts.Entity.DATA_ID);
5600a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendDataStatusUpdateJoin(sb, projection, Contacts.Entity.DATA_ID);
5601a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5602a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setTables(sb.toString());
5603a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        qb.setProjectionMap(sEntityProjectionMap);
5604a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        appendAccountFromParameter(qb, uri);
5605a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5606a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5607a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactStatusUpdateJoin(StringBuilder sb, String[] projection,
5608a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String lastStatusUpdateIdColumn) {
5609a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5610a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS,
5611a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
5612a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
5613a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
5614a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP)) {
5615a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES + " "
5616a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    + ContactsStatusUpdatesColumns.ALIAS +
5617a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + lastStatusUpdateIdColumn + "="
5618a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + ContactsStatusUpdatesColumns.CONCRETE_DATA_ID + ")");
56190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5620a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
56210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
5622a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataStatusUpdateJoin(StringBuilder sb, String[] projection,
5623a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5624b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov        if (mDbHelper.isInProjection(projection,
56250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS,
56260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
56270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_ICON,
56280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_LABEL,
56290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP)) {
56300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.STATUS_UPDATES +
5631a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdatesColumns.CONCRETE_DATA_ID + "="
5632a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + dataIdColumn + ")");
56330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        }
5634a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5635a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5636a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
5637a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String contactIdColumn) {
5638a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection,
5639a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Contacts.CONTACT_PRESENCE, Contacts.CONTACT_CHAT_CAPABILITY)) {
5640a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE +
5641a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + contactIdColumn + " = "
5642a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                            + AggregatedPresenceColumns.CONCRETE_CONTACT_ID + ")");
5643a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5644a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5645a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5646a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private void appendDataPresenceJoin(StringBuilder sb, String[] projection,
5647a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            String dataIdColumn) {
5648a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (mDbHelper.isInProjection(projection, Data.PRESENCE, Data.CHAT_CAPABILITY)) {
5649a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE +
5650a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                    " ON (" + StatusUpdates.DATA_ID + "=" + dataIdColumn + ")");
5651a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5652a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    }
5653a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5654a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov    private boolean shouldExcludeRestrictedData(Uri uri) {
5655a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        // Note: currently, "export only" equals to "restricted", but may not in the future.
5656a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        boolean excludeRestrictedData = readBooleanQueryParameter(uri,
5657a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                Data.FOR_EXPORT_ONLY, false);
5658a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (excludeRestrictedData) {
5659a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return true;
5660a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5661a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5662a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        String requestingPackage = getQueryParameter(uri,
5663a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov                ContactsContract.REQUESTING_PACKAGE_PARAM_KEY);
5664a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        if (requestingPackage != null) {
5665a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            return !mDbHelper.hasAccessToRestrictedData(requestingPackage);
5666a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        }
5667a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov
5668a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov        return false;
56690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
56700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
56714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private void appendAccountFromParameter(SQLiteQueryBuilder qb, Uri uri) {
5672f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5673f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5674e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5675e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5676e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5677e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5678fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5679fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5680e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5681e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5682e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5683e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5684e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5685e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
56864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere(RawContacts.ACCOUNT_NAME + "="
56874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
56884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + RawContacts.ACCOUNT_TYPE + "="
56894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                    + DatabaseUtils.sqlEscapeString(accountType));
56904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        } else {
56914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            qb.appendWhere("1");
56924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
56934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
56944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
5695e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    private String appendAccountToSelection(Uri uri, String selection) {
5696f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountName = getQueryParameter(uri, RawContacts.ACCOUNT_NAME);
5697f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final String accountType = getQueryParameter(uri, RawContacts.ACCOUNT_TYPE);
5698e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5699e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
5700e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (partialUri) {
5701e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            // Throw when either account is incomplete
5702fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov            throw new IllegalArgumentException(mDbHelper.exceptionMessage(
5703fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                    "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
5704e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        }
5705e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
5706e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // Accounts are valid by only checking one parameter, since we've
5707e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        // already ruled out partial accounts.
5708e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        final boolean validAccount = !TextUtils.isEmpty(accountName);
5709e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        if (validAccount) {
5710e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "="
5711e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountName) + " AND "
5712e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + RawContacts.ACCOUNT_TYPE + "="
5713e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    + DatabaseUtils.sqlEscapeString(accountType));
5714e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (!TextUtils.isEmpty(selection)) {
5715e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(" AND (");
5716e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(selection);
5717e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                selectionSb.append(')');
5718e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
5719e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selectionSb.toString();
5720e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } else {
5721e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            return selection;
5722e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
5723e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong    }
5724e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
57257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5726c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * Gets the value of the "limit" URI query parameter.
5727c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *
5728c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     * @return A string containing a non-negative integer, or <code>null</code> if
5729c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     *         the parameter is not set, or is set to an invalid value.
5730c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov     */
5731f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private String getLimit(Uri uri) {
57322e40e351a80ff608045bc9f55b48bd1a3d16926bDmitri Plotnikov        String limitParam = getQueryParameter(uri, ContactsContract.LIMIT_PARAM_KEY);
5733c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        if (limitParam == null) {
5734c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5735c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5736c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        // make sure that the limit is a non-negative integer
5737c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        try {
5738c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            int l = Integer.parseInt(limitParam);
5739c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            if (l < 0) {
5740c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                Log.w(TAG, "Invalid limit parameter: " + limitParam);
5741c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return null;
5742c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            }
5743c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return String.valueOf(l);
5744c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        } catch (NumberFormatException ex) {
5745c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            Log.w(TAG, "Invalid limit parameter: " + limitParam);
5746c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            return null;
5747c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        }
5748c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
5749c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
575000ec508630251d6c6e3746469c9428f5a8cd5996Jeff Sharkey    String getContactsRestrictions() {
5751d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
575270b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
575370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        } else {
5754fada1f08e7ffc8012bf2175f61f3ef3270eba9ecDmitri Plotnikov            return RawContactsColumns.CONCRETE_IS_RESTRICTED + "=0";
575570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        }
575670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    }
575770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
575870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov    public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) {
5759d91272b48f97243533c6580981e12a4847b5783fJeff Hamilton        if (mDbHelper.hasAccessToRestrictedData()) {
576070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov            return "1";
576167dde51ab932dc84d95a203b113989b13437f13dJeff Sharkey        } else {
57625ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS
57635ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                    + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + contactIdColumn + ")=0";
5764619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
5765619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
5766619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
5767b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    @Override
5768ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
5769b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        int match = sUriMatcher.match(uri);
5770b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        switch (match) {
5771a549eb3c9627862a3e45d910d5c981191086a949Dmitri Plotnikov            case CONTACTS_ID_PHOTO: {
5772ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert                return openPhotoFile(uri, mode,
5773e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=" + Contacts.PHOTO_ID + " AND " + RawContacts.CONTACT_ID + "=?",
5774e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5775e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            }
5776b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5777e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            case DATA_ID: {
5778ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert                return openPhotoFile(uri, mode,
5779e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                        Data._ID + "=? AND " + Data.MIMETYPE + "='" + Photo.CONTENT_ITEM_TYPE + "'",
57804da1b8ded4435a3392bac3511c67182015e0953fDmitri Plotnikov                        new String[]{uri.getPathSegments().get(1)});
5781d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5782d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5783f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD: {
578442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
578542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                mSelectionArgs1[0] = String.valueOf(lookupContactIdByLookupKey(mDb, lookupKey));
578642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + "=?";
578742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
578842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // When opening a contact as file, we pass back contents as a
578942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // vCard-encoded stream. We build into a local buffer first,
579042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                // then pipe into MemoryFile once the exact size is known.
579142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
579242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                outputRawContactsAsVCard(localStream, selection, mSelectionArgs1);
5793ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert                return buildFileDescriptor(localStream);
579442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            }
579542aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann
579642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD: {
579742aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String lookupKeys = uri.getPathSegments().get(2);
579842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String[] loopupKeyList = lookupKeys.split(":");
579942aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final StringBuilder inBuilder = new StringBuilder();
580042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                int index = 0;
5801d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // SQLite has limits on how many parameters can be used
5802d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                // so the IDs are concatenated to a query string here instead
580342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                for (String lookupKey : loopupKeyList) {
580442aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    if (index == 0) {
5805d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append("(");
580642aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    } else {
5807d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                        inBuilder.append(",");
580842aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    }
5809d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                    inBuilder.append(lookupContactIdByLookupKey(mDb, lookupKey));
581042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                    index++;
581142aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                }
581242aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                inBuilder.append(')');
581342aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann                final String selection = Contacts._ID + " IN " + inBuilder.toString();
5814d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5815d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // When opening a contact as file, we pass back contents as a
5816d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // vCard-encoded stream. We build into a local buffer first,
5817d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                // then pipe into MemoryFile once the exact size is known.
5818d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                final ByteArrayOutputStream localStream = new ByteArrayOutputStream();
5819d5a176cfe6d8701ae8b7882596711e5fc2746be1Daniel Lehmann                outputRawContactsAsVCard(localStream, selection, null);
5820ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert                return buildFileDescriptor(localStream);
5821d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5822b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5823b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov            default:
5824fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                throw new FileNotFoundException(mDbHelper.exceptionMessage("File does not exist",
5825fa4a38c9d54f3e3aad4674867bb1250f450c0b95Dmitri Plotnikov                        uri));
5826b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov        }
5827b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov    }
5828b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
5829ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert    private ParcelFileDescriptor openPhotoFile(Uri uri, String mode, String selection,
5830e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            String[] selectionArgs)
5831e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throws FileNotFoundException {
5832e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        if (!"r".equals(mode)) {
5833e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov            throw new FileNotFoundException(mDbHelper.exceptionMessage("Mode " + mode
5834e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                    + " not supported.", uri));
5835e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        }
5836e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5837e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        String sql =
5838e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() +
5839e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov                " WHERE " + selection;
5840e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov        SQLiteDatabase db = mDbHelper.getReadableDatabase();
5841ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert        return DatabaseUtils.blobFileDescriptorForQuery(db, sql, selectionArgs);
5842e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov    }
5843e8d2c8276d6331843410c97751e46fc50b257379Dmitri Plotnikov
5844d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private static final String CONTACT_MEMORY_FILE_NAME = "contactAssetFile";
5845d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5846d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5847ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert     * Returns a {@link ParcelFileDescriptor} backed by the
5848d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * contents of the given {@link ByteArrayOutputStream}.
5849d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5850ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert    private ParcelFileDescriptor buildFileDescriptor(ByteArrayOutputStream stream) {
5851d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        try {
5852d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            stream.flush();
5853d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5854d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            final byte[] byteData = stream.toByteArray();
5855d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5856ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return ParcelFileDescriptor.fromData(byteData, CONTACT_MEMORY_FILE_NAME);
5857d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        } catch (IOException e) {
5858ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            Log.w(TAG, "Problem writing stream into an ParcelFileDescriptor: " + e.toString());
5859ac13ddd04d665442de846b59234bdc936a6699b4Bjorn Bringert            return null;
5860d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5861d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5862d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5863d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    /**
5864d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * Output {@link RawContacts} matching the requested selection in the vCard
5865d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * format to the given {@link OutputStream}. This method returns silently if
5866d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     * any errors encountered.
5867d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey     */
5868d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    private void outputRawContactsAsVCard(OutputStream stream, String selection,
5869d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            String[] selectionArgs) {
5870d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        final Context context = this.getContext();
58717a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        final VCardComposer composer =
58727a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa                new VCardComposer(context, VCardConfig.VCARD_TYPE_DEFAULT, false);
5873d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.addHandler(composer.new HandlerForOutputStream(stream));
5874d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5875f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey        // No extra checks since composer always uses restricted views
58767a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        if (!composer.init(selection, selectionArgs)) {
58777a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa            Log.w(TAG, "Failed to init VCardComposer");
5878d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            return;
58797a2a564e9a72969999821142c821eb1b912f0d95Daisuke Miyakawa        }
5880d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey
5881d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        while (!composer.isAfterLast()) {
5882d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            if (!composer.createOneEntry()) {
5883d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey                Log.w(TAG, "Failed to output a contact.");
5884d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey            }
5885d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        }
5886d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey        composer.terminate();
5887d173ca0f279d89030cc7e3f1aadd18755a2e8766Jeff Sharkey    }
5888b3f909fee75cb384fc381ec5ce70dd001669f945Dmitri Plotnikov
58894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
58904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
5891a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
58924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
5893b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS:
5894be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return Contacts.CONTENT_TYPE;
58952d2ec88b7af615b2f05e987da45425be9cace1baTom O'Neill            case CONTACTS_LOOKUP:
5896b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_ID:
5897b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
5898b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_ITEM_TYPE;
5899f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey            case CONTACTS_AS_VCARD:
590042aff67de3f0f4b8664a74fe6ff63ae191aa51bfDaniel Lehmann            case CONTACTS_AS_MULTI_VCARD:
5901f9aeb84d61c01a473819e9173f8311ca5d678a8dJeff Sharkey                return Contacts.CONTENT_VCARD_TYPE;
5902f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov            case CONTACTS_ID_PHOTO:
5903f06a42ca707b8c74d0ac9070db5f9767f4fe74ddDmitri Plotnikov                return "image/png";
5904b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS:
5905be84be1519fe0b73f47c2b2fe9badb8a3e833b28Dmitri Plotnikov                return RawContacts.CONTENT_TYPE;
5906b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case RAW_CONTACTS_ID:
5907b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return RawContacts.CONTENT_ITEM_TYPE;
5908508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
5909b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikov                return mDbHelper.getDataMimeType(ContentUris.parseId(uri));
591048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES:
591148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_TYPE;
591248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case PHONES_ID:
591348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Phone.CONTENT_ITEM_TYPE;
59149005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov            case PHONE_LOOKUP:
59159005e312949b4624aae6953dbdab2eaee1650835Dmitri Plotnikov                return PhoneLookup.CONTENT_TYPE;
591648828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS:
591748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_TYPE;
591848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case EMAILS_ID:
591948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return Email.CONTENT_ITEM_TYPE;
592048828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS:
592148828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_TYPE;
592248828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov            case POSTALS_ID:
592348828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                return StructuredPostal.CONTENT_ITEM_TYPE;
5924b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
5925b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_TYPE;
5926b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
5927b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return AggregationExceptions.CONTENT_ITEM_TYPE;
5928b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case SETTINGS:
5929b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Settings.CONTENT_TYPE;
5930b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
5931b4cfef6f658ecade496351107f6ed2a4818f3e3aDmitri Plotnikov                return Contacts.CONTENT_TYPE;
5932c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SUGGESTIONS:
5933c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SUGGEST_MIME_TYPE;
5934c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov            case SEARCH_SHORTCUT:
5935c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov                return SearchManager.SHORTCUT_MIME_TYPE;
5936d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES:
5937d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_TYPE;
5938d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov            case DIRECTORIES_ID:
5939d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov                return Directory.CONTENT_ITEM_TYPE;
594061efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov            default:
594161efab87c2c8166b3cd69ed1a908d1c0d7271d0bDmitri Plotnikov                return mLegacyApiSupport.getType(uri);
59424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
59434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
59447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
594509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    public String[] getDefaultProjection(Uri uri) {
594609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        final int match = sUriMatcher.match(uri);
594709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        switch (match) {
594809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS:
594909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP:
595009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_ID:
595109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_LOOKUP_ID:
595209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_SUGGESTIONS:
595309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsProjectionMap.getColumnNames();
595409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
59558727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov            case CONTACTS_ID_ENTITIES:
59568727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov                return sEntityProjectionMap.getColumnNames();
59578727a729d5c0e875538025f0a85b3ac64c3a7745Dmitri Plotnikov
595809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_VCARD:
595909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case CONTACTS_AS_MULTI_VCARD:
596009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sContactsVCardProjectionMap.getColumnNames();
596109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
596209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS:
596309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case RAW_CONTACTS_ID:
596409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sRawContactsProjectionMap.getColumnNames();
596509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
596609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DATA_ID:
596709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES:
596809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONES_ID:
596909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS:
597009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case EMAILS_ID:
597109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS:
597209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case POSTALS_ID:
597309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDataProjectionMap.getColumnNames();
597409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
597509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case PHONE_LOOKUP:
597609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sPhoneLookupProjectionMap.getColumnNames();
597709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
597809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTIONS:
597909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case AGGREGATION_EXCEPTION_ID:
598009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sAggregationExceptionsProjectionMap.getColumnNames();
598109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
598209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case SETTINGS:
598309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sSettingsProjectionMap.getColumnNames();
598409ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
598509ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES:
598609ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            case DIRECTORIES_ID:
598709ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return sDirectoryProjectionMap.getColumnNames();
598809ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
598909ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov            default:
599009ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov                return null;
599109ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov        }
599209ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov    }
599309ae48b82b17e24016b14a1ab64706222ab1071fDmitri Plotnikov
59945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void setDisplayName(long rawContactId, int displayNameSource,
59955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            String displayNamePrimary, String displayNameAlternative, String phoneticName,
59965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            int phoneticNameStyle, String sortKeyPrimary, String sortKeyAlternative) {
59975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(1, displayNameSource);
59985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 2, displayNamePrimary);
59995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 3, displayNameAlternative);
60005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 4, phoneticName);
60015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(5, phoneticNameStyle);
60025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 6, sortKeyPrimary);
60035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mRawContactDisplayNameUpdate, 7, sortKeyAlternative);
60045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mRawContactDisplayNameUpdate.bindLong(8, rawContactId);
600525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        mRawContactDisplayNameUpdate.execute();
60063cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov    }
60073cebbf7141252768d3e272e049e9c5b0cb9d710eDmitri Plotnikov
600873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    /**
600973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     * Sets the {@link RawContacts#DIRTY} for the specified raw contact.
601073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov     */
601173776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    private void setRawContactDirty(long rawContactId) {
6012a8d8b1cb48a6e94645dbce836193b40c7481356cDmitri Plotnikov        mDirtyRawContacts.add(rawContactId);
601373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
601473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov
6015c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
6016c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
6017c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
6018c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
6019c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
6020c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
6021653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsPrimary(long rawContactId, long dataId, long mimeTypeId) {
6022c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
6023653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(2, mimeTypeId);
6024653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetPrimaryStatement.bindLong(3, rawContactId);
6025c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
6026c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
6027c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
6028c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
6029c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
6030c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
6031c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
6032c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
6033c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
6034653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    private void setIsSuperPrimary(long rawContactId, long dataId, long mimeTypeId) {
6035c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
6036653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(2, mimeTypeId);
6037653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov        mSetSuperPrimaryStatement.bindLong(3, rawContactId);
6038c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
6039c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
6040ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
6041813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov    public String insertNameLookupForEmail(long rawContactId, long dataId, String email) {
6042f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(email)) {
6043813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return null;
6044f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6045f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6046b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov        String address = mDbHelper.extractHandleFromEmailAddress(email);
6047b06484032125877d1a89785a1a912ca58c12d448Dmitri Plotnikov        if (address == null) {
6048813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov            return null;
6049f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6050f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6051f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
6052f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
6053813fd5712e0ad264ff6907c85d68a01fb1255d28Dmitri Plotnikov        return address;
6054f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6055f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6056f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
6057f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Normalizes the nickname and inserts it in the name lookup table.
6058f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
6059f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
6060f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (TextUtils.isEmpty(nickname)) {
6061f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
6062f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6063f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6064f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNameLookup(rawContactId, dataId,
6065f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
6066f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6067f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6068a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    public void insertNameLookupForOrganization(long rawContactId, long dataId, String company,
6069a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            String title) {
6070a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(company)) {
6071a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
6072a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(company));
6073a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
6074a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        if (!TextUtils.isEmpty(title)) {
6075a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka            insertNameLookup(rawContactId, dataId,
6076a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                    NameLookupType.ORGANIZATION, NameNormalizer.normalize(title));
6077a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        }
6078a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka    }
6079f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6080d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov    public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name,
6081d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov            int fullNameStyle) {
6082d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov        mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name, fullNameStyle);
6083f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6084f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6085f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private class StructuredNameLookupBuilder extends NameLookupBuilder {
6086f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6087f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        public StructuredNameLookupBuilder(NameSplitter splitter) {
6088f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            super(splitter);
6089f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6090f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6091f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6092f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
6093f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String name) {
6094f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
6095f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6096f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6097f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        @Override
6098f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        protected String[] getCommonNicknameClusters(String normalizedName) {
6099d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
6100f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
6101f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6102f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
610348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    public void insertNameLookupForPhoneticName(long rawContactId, long dataId,
610448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            ContentValues values) {
610548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (values.containsKey(StructuredName.PHONETIC_FAMILY_NAME)
610648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                || values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)
610748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                || values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME)) {
610848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookupForPhoneticName(rawContactId, dataId,
610948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
611048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
611148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    values.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
611248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
611348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    }
611448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
611548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    public void insertNameLookupForPhoneticName(long rawContactId, long dataId, String familyName,
611648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            String middleName, String givenName) {
611748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        mSb.setLength(0);
611848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (familyName != null) {
611948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(familyName.trim());
612048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
612148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (middleName != null) {
612248786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(middleName.trim());
612348786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
612448786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (givenName != null) {
612548786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            mSb.append(givenName.trim());
612648786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
612748786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
612848786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        if (mSb.length() > 0) {
612948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov            insertNameLookup(rawContactId, dataId, NameLookupType.NAME_COLLATION_KEY,
613048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov                    NameNormalizer.normalize(mSb.toString()));
613148786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov        }
61323b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov
61333b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov        if (givenName != null) {
61343b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            // We want the phonetic given name to be used for search, but not for aggregation,
61353b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            // which is why we are using NAME_SHORTHAND rather than NAME_COLLATION_KEY
61363b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov            insertNameLookup(rawContactId, dataId, NameLookupType.NAME_SHORTHAND,
61373b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov                    NameNormalizer.normalize(givenName.trim()));
61383b10d3a1ed1052dcdf529da370cb71b74164b158Dmitri Plotnikov        }
613948786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov    }
614048786768751cdd9868fb3cf3c82d63f277a54b6fDmitri Plotnikov
6141f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
6142f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
6143f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
6144f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
61455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(1, rawContactId);
61465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(2, dataId);
61475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupInsert.bindLong(3, lookupType);
61485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        bindString(mNameLookupInsert, 4, name);
6149f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupInsert.executeInsert();
6150f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6151f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
6152f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
6153f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
6154f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
6155f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public void deleteNameLookup(long dataId) {
61565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        mNameLookupDelete.bindLong(1, dataId);
6157f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mNameLookupDelete.execute();
6158f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
6159f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
61602d89933b87a15ae5ed5d6b6ec4220ac085695adaDmitri Plotnikov    public void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
6161d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
6162d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                "SELECT DISTINCT " + RawContacts.CONTACT_ID +
6163d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.RAW_CONTACTS +
6164d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " JOIN " + Tables.NAME_LOOKUP +
6165d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " ON(" + RawContactsColumns.CONCRETE_ID + "="
6166d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                        + NameLookupColumns.RAW_CONTACT_ID + ")" +
6167d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE normalized_name GLOB '");
6168e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov        sb.append(NameNormalizer.normalize(filterParam));
6169916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        sb.append("*' AND " + NameLookupColumns.NAME_TYPE +
6170916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov                    " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))");
6171e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov    }
6172e46667e641cd1c60998e1ccab4b60531d5b12ef7Dmitri Plotnikov
61735ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov    public String getRawContactsByFilterAsNestedQuery(String filterParam) {
6174c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        StringBuilder sb = new StringBuilder();
61757318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByFilterAsNestedQuery(sb, filterParam);
6176c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        return sb.toString();
6177c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov    }
6178c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
61797318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov    public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam) {
61807318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov        appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), true);
61815e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
61825e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
61835e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName,
61847318f9e11bdac5ea1ff5e6a8143b90c4e5c497f6Dmitri Plotnikov            boolean allowEmailMatch) {
6185d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov        sb.append("(" +
6186dc947a9d03279eab0fb7c3b9d8ffbb492c1e2062Dmitri Plotnikov                "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
6187d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " FROM " + Tables.NAME_LOOKUP +
6188d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " WHERE " + NameLookupColumns.NORMALIZED_NAME +
6189d60cf9ba1d039f1a22375f56c18356e0d4f8ca14Dmitri Plotnikov                " GLOB '");
61905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        sb.append(normalizedName);
6191a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
6192a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NAME_COLLATION_KEY + ","
6193a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka                + NameLookupType.NICKNAME + ","
61944cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                + NameLookupType.NAME_SHORTHAND + ","
6195f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                + NameLookupType.ORGANIZATION + ","
619689f1f71495aedc58252b3f58a46a036986c319d2Dmitri Plotnikov                + NameLookupType.NAME_CONSONANTS);
619720938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        if (allowEmailMatch) {
619820938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov            sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
619920938cd6df602bf08c232b32fc047592c1561347Dmitri Plotnikov        }
620089f1f71495aedc58252b3f58a46a036986c319d2Dmitri Plotnikov        sb.append("))");
6201ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
6202ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
62034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    /**
62047a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * Takes components of a name from the query parameters and returns a cursor with those
62057a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * components as well as all missing components.  There is no database activity involved
62067a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     * in this so the call can be made on the UI thread.
62077a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov     */
62087a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private Cursor completeName(Uri uri, String[] projection) {
62097a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        if (projection == null) {
62107a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            projection = sDataProjectionMap.getColumnNames();
62117a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
62127a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
62137a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        ContentValues values = new ContentValues();
62147a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        StructuredNameRowHandler handler =
62157a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                (StructuredNameRowHandler) getDataRowHandler(StructuredName.CONTENT_ITEM_TYPE);
62167a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
62177a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        copyQueryParamsToContentValues(values, uri,
62187a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.DISPLAY_NAME,
62197a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PREFIX,
62207a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.GIVEN_NAME,
62217a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.MIDDLE_NAME,
62227a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.FAMILY_NAME,
62237a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.SUFFIX,
62247a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_NAME,
62257a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_FAMILY_NAME,
62267a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_MIDDLE_NAME,
62277a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                StructuredName.PHONETIC_GIVEN_NAME
62287a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        );
62297a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
62307a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        handler.fixStructuredNameComponents(values, values);
62317a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
62327a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        MatrixCursor cursor = new MatrixCursor(projection);
62337a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        Object[] row = new Object[projection.length];
62347a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (int i = 0; i < projection.length; i++) {
62357a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            row[i] = values.get(projection[i]);
62367a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
62377a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        cursor.addRow(row);
62387a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        return cursor;
62397a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
62407a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
62417a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    private void copyQueryParamsToContentValues(ContentValues values, Uri uri, String... columns) {
62427a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        for (String column : columns) {
62437a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            String param = uri.getQueryParameter(column);
62447a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            if (param != null) {
62457a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov                values.put(column, param);
62467a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov            }
62477a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov        }
62487a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    }
62497a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
62507a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov
62517a3c645fa7db38449d34eb04d4e032fd079c3244Dmitri Plotnikov    /**
62524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     * Inserts an argument at the beginning of the selection arg list.
62534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov     */
62544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    private String[] insertSelectionArg(String[] selectionArgs, String arg) {
6255b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
6256b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
6257b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
6258b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
6259b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
62604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            newSelectionArgs[0] = arg;
62614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            System.arraycopy(selectionArgs, 0, newSelectionArgs, 1, selectionArgs.length);
6262b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
6263b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
6264b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
6265caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov
62665e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    private String[] appendProjectionArg(String[] projection, String arg) {
62675e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        if (projection == null) {
62685e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar            return null;
62695e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        }
62705e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        final int length = projection.length;
62715e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        String[] newProjection = new String[length + 1];
62725e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        System.arraycopy(projection, 0, newProjection, 0, length);
62735e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        newProjection[length] = arg;
62745e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar        return newProjection;
62755e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar    }
62765e28b3a5e44bf4f2c0980c50a2ab35350fc5f230Evan Millar
6277caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    protected Account getDefaultAccount() {
6278caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        AccountManager accountManager = AccountManager.get(getContext());
6279caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        try {
6280df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana            Account[] accounts = accountManager.getAccountsByTypeAndFeatures(DEFAULT_ACCOUNT_TYPE,
6281df9fd6b239de5829b04cb413e4dfa3e6da649c38Fred Quintana                    new String[] {FEATURE_LEGACY_HOSTED_OR_GOOGLE}, null, null).getResult();
6282caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            if (accounts != null && accounts.length > 0) {
6283caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov                return accounts[0];
6284caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov            }
6285caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        } catch (Throwable e) {
62866f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov            Log.e(TAG, "Cannot determine the default account for contacts compatibility", e);
6287caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov        }
62886f7446a25ecb55ee213eaa7702837cdf32e68777Dmitri Plotnikov        return null;
6289caa1cf4ef062f163ac5e370cebc0e47b5ae7460eDmitri Plotnikov    }
6290f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
629173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    /**
629273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     * Returns true if the specified account type is writable.
629373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov     */
629473f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov    protected boolean isWritableAccount(String accountType) {
6295bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        if (accountType == null) {
6296bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov            return true;
6297bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov        }
6298bc487a312a84972f03776cdc5784cc132a57f8fdDmitri Plotnikov
629973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        Boolean writable = mAccountWritability.get(accountType);
630073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable != null) {
630173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            return writable;
630273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
630373f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
6304627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        IContentService contentService = ContentResolver.getContentService();
6305627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
6306627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            for (SyncAdapterType sync : contentService.getSyncAdapterTypes()) {
6307627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                if (ContactsContract.AUTHORITY.equals(sync.authority) &&
630873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                        accountType.equals(sync.accountType)) {
630973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    writable = sync.supportsUploading();
631073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov                    break;
6311627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                }
6312627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            }
6313627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        } catch (RemoteException e) {
6314627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            Log.e(TAG, "Could not acquire sync adapter types");
6315627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        }
631673f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
631773f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        if (writable == null) {
631873f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov            writable = false;
631973f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        }
632073f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov
632173f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        mAccountWritability.put(accountType, writable);
632273f1f396c155b247b903d8f4111db17d3e13dc4dDmitri Plotnikov        return writable;
6323627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov    }
6324b4e61e064b59e8076df81b061add9fb358fd2ed9Dmitri Plotnikov
6325d3d812af96f7d77e13dc60652626b39f25907147Dmitri Plotnikov
6326f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter,
6327f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean defaultValue) {
6328f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6329f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // Manually parse the query, which is much faster than calling uri.getQueryParameter
6330f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6331f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6332f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6333f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6334f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6335f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = query.indexOf(parameter);
6336f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (index == -1) {
6337f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return defaultValue;
6338f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6339f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6340f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        index += parameter.length();
6341f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6342f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return !matchQueryParameter(query, index, "=0", false)
6343f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && !matchQueryParameter(query, index, "=false", true);
6344f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6345f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6346f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    private static boolean matchQueryParameter(String query, int index, String value,
6347f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            boolean ignoreCase) {
6348f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int length = value.length();
6349f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return query.regionMatches(ignoreCase, index, value, 0, length)
6350f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                && (query.length() == index + length || query.charAt(index + length) == '&');
6351f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
6352f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6353f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /**
6354f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     * A fast re-implementation of {@link Uri#getQueryParameter}
6355f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov     */
6356f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    /* package */ static String getQueryParameter(Uri uri, String parameter) {
6357f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String query = uri.getEncodedQuery();
6358f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (query == null) {
6359f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            return null;
6360f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6361f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6362f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int queryLength = query.length();
6363f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int parameterLength = parameter.length();
6364f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6365f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        String value;
6366f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int index = 0;
6367f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        while (true) {
6368f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index = query.indexOf(parameter, index);
6369f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (index == -1) {
6370f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
6371f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6372f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6373f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            index += parameterLength;
6374f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6375f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (queryLength == index) {
6376f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                return null;
6377f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6378f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6379f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            if (query.charAt(index) == '=') {
6380f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                index++;
6381f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                break;
6382f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            }
6383f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6384f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6385f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        int ampIndex = query.indexOf('&', index);
6386f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        if (ampIndex == -1) {
6387f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index);
6388f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        } else {
6389f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov            value = query.substring(index, ampIndex);
6390f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        }
6391f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
6392f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        return Uri.decode(value);
6393f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
63945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
63955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void bindString(SQLiteStatement stmt, int index, String value) {
63965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (value == null) {
63975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindNull(index);
63985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
63995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindString(index, value);
64005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
64015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
64025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
64035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void bindLong(SQLiteStatement stmt, int index, Number value) {
64045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        if (value == null) {
64055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindNull(index);
64065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        } else {
64075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov            stmt.bindLong(index, value.longValue());
64085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        }
64095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
64104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
6411