ContactsProvider2.java revision b67163a1088f09c59f324350662eb18772fac6b6
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
1928f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.AggregatesColumns;
2028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.AggregationExceptionColumns;
2128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.Clauses;
2228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.ContactsColumns;
2328f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.ContactOptionsColumns;
2428f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.DataColumns;
2528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.GroupsColumns;
2628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.MimetypesColumns;
2728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.PhoneLookupColumns;
2828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.Tables;
2935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport com.android.internal.content.SyncStateContentProviderHelper;
304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
31b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
3235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.pm.PackageManager;
334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.content.ContentProvider;
3435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.UriMatcher;
35b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.content.Context;
3635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentValues;
3735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport android.content.EntityIterator;
3935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.Entity;
4035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentProviderResult;
417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport android.content.OperationApplicationException;
4235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentProviderOperation;
434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
44ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
45b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
48c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
50619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.os.Binder;
51b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
52508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
53de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract;
546bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikovimport android.provider.Contacts.ContactMethods;
55de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Aggregates;
56b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
57de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds;
58de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Contacts;
59de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Data;
60ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.Groups;
611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkeyimport android.provider.ContactsContract.Presence;
62619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.provider.ContactsContract.RestrictionExceptions;
636bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikovimport android.provider.ContactsContract.Aggregates.AggregationSuggestions;
64ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
65ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
66de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
67ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Postal;
684097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
69a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
70a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
7100d71133c63c882fb41729ddc3a52f66fb155374Evan Millarimport android.util.Log;
724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
74b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
80035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintanapublic class ContactsProvider2 extends ContentProvider {
81b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    // TODO: clean up debug tag and rename this class
82b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private static final String TAG = "ContactsProvider ~~~~";
834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
84619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: define broadcastreceiver to catch app uninstalls that should clear exceptions
85619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
86619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
87619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
88a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
90d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_ORDER_BY = Aggregates.STARRED + " DESC, "
91d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            + Aggregates.TIMES_CONTACTED + " DESC, "
92d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            + Aggregates.DISPLAY_NAME + " ASC";
93d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
94d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            "(SELECT COUNT(1) FROM " + Tables.AGGREGATES + " WHERE "
95d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            + Aggregates.STARRED + "=1) + 25";
96d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int AGGREGATES = 1000;
986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int AGGREGATES_ID = 1001;
996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int AGGREGATES_DATA = 1002;
1001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int AGGREGATES_SUMMARY = 1003;
1011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int AGGREGATES_SUMMARY_ID = 1004;
102ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int AGGREGATES_SUMMARY_FILTER = 1005;
103d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final int AGGREGATES_SUMMARY_STREQUENT = 1006;
104d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final int AGGREGATES_SUMMARY_STREQUENT_FILTER = 1007;
105b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private static final int AGGREGATES_SUMMARY_GROUP = 1008;
1064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS = 2002;
1086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS_ID = 2003;
1096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS_DATA = 2004;
11028ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey    private static final int CONTACTS_FILTER_EMAIL = 2005;
1114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
114ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
115ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES_FILTER = 3003;
116ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int POSTALS = 3004;
117a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
120b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
121b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
122b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE = 7000;
1241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE_ID = 7001;
1251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
12631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
12731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
128619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private static final int RESTRICTION_EXCEPTIONS = 9000;
129619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
130ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
132ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
133ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
13435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
13535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
136ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private interface Projections {
137ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String[] PROJ_CONTACTS = new String[] {
138ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            ContactsColumns.CONCRETE_ID,
139ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
140ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
141ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String[] PROJ_DATA_CONTACTS = new String[] {
142ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                ContactsColumns.CONCRETE_ID,
143ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                DataColumns.CONCRETE_ID,
144ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Contacts.AGGREGATE_ID,
145ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                ContactsColumns.PACKAGE_ID,
146ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Contacts.IS_RESTRICTED,
147ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Data.MIMETYPE,
148ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
149ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
150ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_CONTACT_ID = 0;
151ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_DATA_ID = 1;
152ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_AGGREGATE_ID = 2;
153ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_PACKAGE_ID = 3;
154ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_IS_RESTRICTED = 4;
155ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_MIMETYPE = 5;
156ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
157ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String[] PROJ_DATA_AGGREGATES = new String[] {
158ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            ContactsColumns.CONCRETE_ID,
159ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                DataColumns.CONCRETE_ID,
160ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.CONCRETE_ID,
161ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                MimetypesColumns.CONCRETE_ID,
162ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Phone.NUMBER,
163ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Email.DATA,
164ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID,
165ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID,
166ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID,
167ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID,
168ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
169ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
170ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_MIMETYPE_ID = 3;
171ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_PHONE_NUMBER = 4;
172ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_EMAIL_DATA = 5;
173ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_OPTIMAL_PHONE_ID = 6;
174ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_FALLBACK_PHONE_ID = 7;
175ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_OPTIMAL_EMAIL_ID = 8;
176ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_FALLBACK_EMAIL_ID = 9;
17780c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
178ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
18031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
18131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
18231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /** Contains just the contacts columns */
1846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final HashMap<String, String> sAggregatesProjectionMap;
18500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar    /** Contains the aggregate columns along with primary phone */
1861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final HashMap<String, String> sAggregatesSummaryProjectionMap;
187de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar    /** Contains the data, contacts, and aggregate columns, for joined tables. */
188de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar    private static final HashMap<String, String> sDataContactsAggregateProjectionMap;
189a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the contacts columns */
1904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
191a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the data columns */
1924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sDataProjectionMap;
193a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains the data and contacts columns, for joined tables */
194a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final HashMap<String, String> sDataContactsProjectionMap;
195ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
196ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
197ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
198ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
199b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    /** Contains the just the agg_exceptions columns */
200b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
201619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /** Contains the just the {@link RestrictionExceptions} columns */
202619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private static final HashMap<String, String> sRestrictionExceptionsProjectionMap;
2037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
204c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the contact id associated with a data record. */
205c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdSelect;
206c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the mimetype id associated with a data record. */
207c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedMimetypeSelect;
208c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the aggregate id associated with a contact record. */
209c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedAggregateIdSelect;
210c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns a list of contact ids associated with an aggregate record. */
211c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdListSelect;
212c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
213c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "primary" is selected.*/
214c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetPrimaryWhere;
215c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
216c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "super primary" is selected.*/
217c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetSuperPrimaryWhere;
218b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    /** Sql where statement for filtering on groups. */
219b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private static final String sAggregatesInGroupSelect;
220c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
221c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
222c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precomipled sql statement for setting a data record to the super primary. */
223c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
224a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final String GTALK_PROTOCOL_STRING = ContactMethods
2261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            .encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
2271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
2294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
230a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
2316bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates", AGGREGATES);
2326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#", AGGREGATES_ID);
2336bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#/data", AGGREGATES_DATA);
2341f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary", AGGREGATES_SUMMARY);
2351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/#", AGGREGATES_SUMMARY_ID);
236ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/filter/*",
237ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                AGGREGATES_SUMMARY_FILTER);
238d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/strequent/",
239d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                AGGREGATES_SUMMARY_STREQUENT);
240d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/strequent/filter/*",
241d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                AGGREGATES_SUMMARY_STREQUENT_FILTER);
242b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/group/*",
243b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                AGGREGATES_SUMMARY_GROUP);
24431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#/suggestions",
24531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
2464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
2474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
248a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
249b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter_email/*",
250b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                CONTACTS_FILTER_EMAIL);
251b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2524f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
2534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
254ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
255ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
256ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
2571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
258ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
259ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
260ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
261ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
26235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
26335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
264a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
265b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
266b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
267b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
268b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
2694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
270bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence", PRESENCE);
271bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence/#", PRESENCE_ID);
2721f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
273619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "restriction_exceptions", RESTRICTION_EXCEPTIONS);
274619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
275fec4e13316f2731d84394e5fa2f93af3febdc20cEvan Millar        HashMap<String, String> columns;
2764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2776bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        // Aggregates projection map
2786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns = new HashMap<String, String>();
27900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(Aggregates._ID, "aggregates._id AS _id");
2806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Aggregates.DISPLAY_NAME, Aggregates.DISPLAY_NAME);
2816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Aggregates.LAST_TIME_CONTACTED, Aggregates.LAST_TIME_CONTACTED);
282d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        columns.put(Aggregates.TIMES_CONTACTED, Aggregates.TIMES_CONTACTED);
2836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Aggregates.STARRED, Aggregates.STARRED);
284ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Aggregates.IN_VISIBLE_GROUP, Aggregates.IN_VISIBLE_GROUP);
285ae6ca1f34cf5458d79ec803411d4308879a91e92Evan Millar        columns.put(Aggregates.PHOTO_ID, Aggregates.PHOTO_ID);
286de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.put(Aggregates.PRIMARY_PHONE_ID, Aggregates.PRIMARY_PHONE_ID);
287c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Aggregates.PRIMARY_EMAIL_ID, Aggregates.PRIMARY_EMAIL_ID);
288d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        columns.put(Aggregates.CUSTOM_RINGTONE, Aggregates.CUSTOM_RINGTONE);
289d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        columns.put(Aggregates.SEND_TO_VOICEMAIL, Aggregates.SEND_TO_VOICEMAIL);
290619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID,
291619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID);
292619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID,
293619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID);
2946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        sAggregatesProjectionMap = columns;
2956bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
2961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Aggregates primaries projection map. The overall presence status is
2971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // the most-present value, as indicated by the largest value.
2981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns = new HashMap<String, String>();
2991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns.putAll(sAggregatesProjectionMap);
30000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.TYPE, CommonDataKinds.Phone.TYPE);
30100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.LABEL, CommonDataKinds.Phone.LABEL);
30200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.NUMBER, CommonDataKinds.Phone.NUMBER);
303bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        columns.put(Presence.PRESENCE_STATUS, "MAX(" + Presence.PRESENCE_STATUS + ")");
3041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        sAggregatesSummaryProjectionMap = columns;
30500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts projection map
3074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
3084f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Contacts._ID, "contacts._id AS _id");
309619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(Contacts.PACKAGE, Contacts.PACKAGE);
3106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Contacts.AGGREGATE_ID, Contacts.AGGREGATE_ID);
311035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Contacts.ACCOUNT_NAME, Contacts.ACCOUNT_NAME);
312035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Contacts.ACCOUNT_TYPE, Contacts.ACCOUNT_TYPE);
3137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Contacts.SOURCE_ID, Contacts.SOURCE_ID);
3147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Contacts.VERSION, Contacts.VERSION);
3157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Contacts.DIRTY, Contacts.DIRTY);
3164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        sContactsProjectionMap = columns;
3174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
3184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Data projection map
3194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
3204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Data._ID, "data._id AS _id");
3214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Data.CONTACT_ID, Data.CONTACT_ID);
322508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        columns.put(Data.MIMETYPE, Data.MIMETYPE);
323c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
324c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
325f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
3267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA1, "data.data1 as data1");
3277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA2, "data.data2 as data2");
3287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA3, "data.data3 as data3");
3297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA4, "data.data4 as data4");
3307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA5, "data.data5 as data5");
3317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA6, "data.data6 as data6");
3327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA7, "data.data7 as data7");
3337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA8, "data.data8 as data8");
3347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA9, "data.data9 as data9");
3357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA10, "data.data10 as data10");
336d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        // Mappings used for backwards compatibility.
337d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        columns.put("number", Phone.NUMBER);
3384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        sDataProjectionMap = columns;
339a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
340a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        // Data and contacts projection map for joins. _id comes from the data table
341a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns = new HashMap<String, String>();
342a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns.putAll(sContactsProjectionMap);
343a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns.putAll(sDataProjectionMap); // _id will be replaced with the one from data
344ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Data.CONTACT_ID, DataColumns.CONCRETE_CONTACT_ID);
345a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        sDataContactsProjectionMap = columns;
3467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
347de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        // Data and contacts projection map for joins. _id comes from the data table
348de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns = new HashMap<String, String>();
349de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.putAll(sAggregatesProjectionMap);
3507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.putAll(sContactsProjectionMap); //
351de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.putAll(sDataProjectionMap); // _id will be replaced with the one from data
352ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Data.CONTACT_ID, DataColumns.CONCRETE_CONTACT_ID);
353de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        sDataContactsAggregateProjectionMap = columns;
354c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
355ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
356ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
357ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups._ID, "groups._id AS _id");
358035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
359035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
360ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.PACKAGE, Groups.PACKAGE);
361ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.PACKAGE_ID, GroupsColumns.CONCRETE_PACKAGE_ID);
362ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
363ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE_RESOURCE, Groups.TITLE_RESOURCE);
364ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
365ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
366ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
367ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Contacts and groups projection map
368ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
369ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
370ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
371ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + AggregatesColumns.CONCRETE_ID
372ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES + " WHERE "
373ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
374ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
375ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
376ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
377ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + AggregatesColumns.CONCRETE_ID + ") FROM "
378ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES + " WHERE "
379ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
380ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + " AND " + Clauses.HAS_PRIMARY_PHONE + ") AS " + Groups.SUMMARY_WITH_PHONES);
381ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
382ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
383ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
384b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
385b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
386b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
387b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
388127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        columns.put(AggregationExceptions.AGGREGATE_ID,
389127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                "contacts1." + Contacts.AGGREGATE_ID + " AS " + AggregationExceptions.AGGREGATE_ID);
390127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        columns.put(AggregationExceptions.CONTACT_ID, AggregationExceptionColumns.CONTACT_ID2);
391b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
392b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
393619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Restriction exception projection map
394619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns = new HashMap<String, String>();
395619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(RestrictionExceptions.PACKAGE_PROVIDER, RestrictionExceptions.PACKAGE_PROVIDER);
396619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(RestrictionExceptions.PACKAGE_CLIENT, RestrictionExceptions.PACKAGE_CLIENT);
397619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(RestrictionExceptions.ALLOW_ACCESS, "1"); // Access granted if row returned
398619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        sRestrictionExceptionsProjectionMap = columns;
399619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
400c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedContactIdSelect = "SELECT " + Data.CONTACT_ID + " FROM " + Tables.DATA + " WHERE "
401c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + Data._ID + "=?";
402c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedMimetypeSelect = "SELECT " + DataColumns.MIMETYPE_ID + " FROM " + Tables.DATA
403c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Data._ID + "=?";
404c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedAggregateIdSelect = "SELECT " + Contacts.AGGREGATE_ID + " FROM " + Tables.CONTACTS
405c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Contacts._ID + "=(" + sNestedContactIdSelect + ")";
406c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedContactIdListSelect = "SELECT " + Contacts._ID + " FROM " + Tables.CONTACTS
407c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Contacts.AGGREGATE_ID + "=(" + sNestedAggregateIdSelect + ")";
408c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sSetPrimaryWhere = Data.CONTACT_ID + "=(" + sNestedContactIdSelect + ") AND "
409c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
410b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        sSetSuperPrimaryWhere = Data.CONTACT_ID + " IN (" + sNestedContactIdListSelect + ") AND "
411c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
412b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        sAggregatesInGroupSelect = AggregatesColumns.CONCRETE_ID + " IN (SELECT "
413b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Contacts.AGGREGATE_ID + " FROM " + Tables.CONTACTS + " WHERE ("
414b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + ContactsColumns.CONCRETE_ID + " IN (SELECT " + Tables.DATA + "."
415b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Data.CONTACT_ID + " FROM " + Tables.DATA_JOIN_MIMETYPES + " WHERE ("
416b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE + "' AND "
417b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + GroupMembership.GROUP_ROW_ID + "=(SELECT " + Tables.GROUPS + "."
418b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Groups._ID + " FROM " + Tables.GROUPS + " WHERE " + Groups.TITLE + "=?)))))";
4194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
4204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
42153056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    private final ContactAggregationScheduler mAggregationScheduler;
4224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private OpenHelper mOpenHelper;
42331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
424a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    private ContactAggregator mContactAggregator;
4254097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
426a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
427a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public ContactsProvider2() {
42853056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        this(new ContactAggregationScheduler());
429a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
430a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
431a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
432a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Constructor for testing.
433a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
43453056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    /* package */ ContactsProvider2(ContactAggregationScheduler scheduler) {
43553056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mAggregationScheduler = scheduler;
436a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
4374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
4384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
4394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
440b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        final Context context = getContext();
44135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
44231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        mOpenHelper = getOpenHelper(context);
4431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
4444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
44553056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mContactAggregator = new ContactAggregator(context, mOpenHelper, mAggregationScheduler);
446a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
447c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
448c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_PRIMARY
449c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetPrimaryWhere);
450c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
451c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_SUPER_PRIMARY
452c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetSuperPrimaryWhere);
453a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
45428f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
45528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
45628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
45728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
45828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_conjunctions));
4594097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
4601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
4614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
4624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
46331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
46431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    protected OpenHelper getOpenHelper(final Context context) {
46531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        return OpenHelper.getInstance(context);
46631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
46731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
468a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
469a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    protected void finalize() throws Throwable {
470a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (mContactAggregator != null) {
471a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.quit();
472a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
473a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
474a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        super.finalize();
475a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
476a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
477a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
478a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
479a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
480a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
481a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        mOpenHelper.wipeData();
482a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
483a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
484a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
485a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Called when a change has been made.
486a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
487a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param uri the uri that the change was made to
488a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
489a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private void onChange(Uri uri) {
490a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null);
491a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
492a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
4944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean isTemporary() {
4954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return false;
4964f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
4974f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
4984f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
4994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Uri insert(Uri uri, ContentValues values) {
500a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
501a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
50235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
503a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
50435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
50535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                id = mOpenHelper.getSyncState().insert(mOpenHelper.getWritableDatabase(), values);
50635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
50735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
5086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: {
5096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                id = insertAggregate(values);
5106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
5116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
5126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
513a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS: {
514f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                final Account account = readAccountFromQueryParams(uri);
515f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                id = insertContact(values, account);
516a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
517a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
518a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
519a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS_DATA: {
520a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                values.put(Data.CONTACT_ID, uri.getPathSegments().get(1));
521035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
522a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
523a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
524a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
525a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
526035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
527a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
528a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
529a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
530ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
531ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final Account account = readAccountFromQueryParams(uri);
532ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                id = insertGroup(values, account);
533ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
534ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
535ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5361f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
5371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                id = insertPresence(values);
5381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
5391f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
5401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
541a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
542508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                throw new UnsupportedOperationException("Unknown uri: " + uri);
543a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
544a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
5467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
5477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
5487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
5497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final Uri result = ContentUris.withAppendedId(uri, id);
550a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        onChange(result);
551a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return result;
552a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
553a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
554a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
555035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
556035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
557035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param values the ContentValues to read from and update
558035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param account the explicitly provided Account
559035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @return false if the accounts are inconsistent
5607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
561035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private boolean resolveAccount(ContentValues values, Account account) {
562035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        // If either is specified then both must be specified.
563035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String accountName = values.getAsString(Contacts.ACCOUNT_NAME);
564035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String accountType = values.getAsString(Contacts.ACCOUNT_TYPE);
565035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (!TextUtils.isEmpty(accountName) || !TextUtils.isEmpty(accountType)) {
566035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final Account valuesAccount = new Account(accountName, accountType);
567035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (account != null && !valuesAccount.equals(account)) {
568035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
569035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
570035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            account = valuesAccount;
571035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
572035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (account != null) {
573035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            values.put(Contacts.ACCOUNT_NAME, account.mName);
574035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            values.put(Contacts.ACCOUNT_TYPE, account.mType);
575035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
576035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
5777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
5787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
5797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * Inserts an item in the aggregates table
5816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
5826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
5836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
5846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
5856bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private long insertAggregate(ContentValues values) {
586a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregates are created automatically");
5876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
5886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
5896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
590a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
591a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
592a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
593f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana     * @param account the account this contact should be associated with. may be null.
594a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
595a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
596f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private long insertContact(ContentValues values, Account account) {
5976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        /*
5986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * The contact record is inserted in the contacts table, but it needs to
5996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * be processed by the aggregator before it will be returned by the
6006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * "aggregates" queries.
6016bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         */
602a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
6036bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
604a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ContentValues overriddenValues = new ContentValues(values);
605a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        overriddenValues.putNull(Contacts.AGGREGATE_ID);
606f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (!resolveAccount(overriddenValues, account)) {
6077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
6087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
6097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
610619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Replace package with internal mapping
611619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String packageName = overriddenValues.getAsString(Contacts.PACKAGE);
612619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        overriddenValues.put(ContactsColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
613619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        overriddenValues.remove(Contacts.PACKAGE);
614619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
615a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        long rowId = db.insert(Tables.CONTACTS, Contacts.AGGREGATE_ID, overriddenValues);
616a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
617a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        mContactAggregator.schedule();
618a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
619a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        return rowId;
620a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
621a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
622a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
623a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
624a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
625a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
626a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
627a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
628035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private long insertData(ContentValues values) {
629a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        boolean success = false;
630a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
631a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
632a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
633a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        db.beginTransaction();
634a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        try {
635a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            long contactId = values.getAsLong(Data.CONTACT_ID);
636a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
637619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            // Replace mimetype with internal mapping
638508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            final String mimeType = values.getAsString(Data.MIMETYPE);
639b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            values.put(DataColumns.MIMETYPE_ID, mOpenHelper.getMimeTypeId(mimeType));
640508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            values.remove(Data.MIMETYPE);
641508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
6424097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
6434097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                parseStructuredName(values);
6444097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            }
6454097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
646508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            // Insert the data row itself
647b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            id = db.insert(Tables.DATA, Data.DATA1, values);
648508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
649a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            // If it's a phone number add the normalized version to the lookup table
6504097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
651508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                final ContentValues phoneValues = new ContentValues();
652508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                final String number = values.getAsString(Phone.NUMBER);
653508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER,
654508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                        PhoneNumberUtils.getStrippedReversed(number));
655508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                phoneValues.put(PhoneLookupColumns.DATA_ID, id);
656a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.CONTACT_ID, contactId);
657b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
658a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
659a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
6606bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov            mContactAggregator.markContactForAggregation(contactId);
661a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
662a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.setTransactionSuccessful();
663a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            success = true;
664a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        } finally {
665a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.endTransaction();
666a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
667a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
668a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (success) {
669a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.schedule();
670a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
671a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
672a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
6734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
6744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
675a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
676ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Delete the given {@link Data} row, fixing up any {@link Aggregates}
677ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * primaries that reference it.
678ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
679ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private int deleteData(long dataId) {
680ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
681ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
682ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimePhone = mOpenHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
683ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimeEmail = mOpenHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
684ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
685ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Check to see if the data about to be deleted was a super-primary on
686ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // the parent aggregate, and set flags to fix-up once deleted.
687ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long aggId = -1;
688ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long mimeId = -1;
689ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        String dataRaw = null;
690ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixOptimal = false;
691ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixFallback = false;
692ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
693ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor cursor = null;
694ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        try {
695ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES,
696ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Projections.PROJ_DATA_AGGREGATES, DataColumns.CONCRETE_ID + "=" + dataId, null,
697ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    null, null, null);
698ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor.moveToFirst()) {
699ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                aggId = cursor.getLong(Projections.COL_AGGREGATE_ID);
700ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mimeId = cursor.getLong(Projections.COL_MIMETYPE_ID);
701ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
702ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    dataRaw = cursor.getString(Projections.COL_PHONE_NUMBER);
703ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixOptimal = (cursor.getLong(Projections.COL_OPTIMAL_PHONE_ID) == dataId);
704ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixFallback = (cursor.getLong(Projections.COL_FALLBACK_PHONE_ID) == dataId);
705ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
706ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    dataRaw = cursor.getString(Projections.COL_EMAIL_DATA);
707ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixOptimal = (cursor.getLong(Projections.COL_OPTIMAL_EMAIL_ID) == dataId);
708ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixFallback = (cursor.getLong(Projections.COL_FALLBACK_EMAIL_ID) == dataId);
709ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
710ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
711ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        } finally {
712ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor != null) {
713ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.close();
714ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor = null;
715ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
716ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
717ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
718ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Delete the requested data item.
719ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        int dataDeleted = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
720ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
721ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Fix-up any super-primary values that are now invalid.
722ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (fixOptimal || fixFallback) {
723ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final ContentValues values = new ContentValues();
724ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final StringBuilder scoreClause = new StringBuilder();
725ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
726ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String SCORE = "score";
727ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
728ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Build scoring clause that will first pick data items under the
729ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // same aggregate that have identical values, otherwise fall back to
730ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // normal primary scoring from the member contacts.
731ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("(CASE WHEN ");
732ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (mimeId == mimePhone) {
733ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Phone.NUMBER);
734ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            } else if (mimeId == mimeEmail) {
735ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Email.DATA);
736ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
737ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("=");
738ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            DatabaseUtils.appendEscapedSQLString(scoreClause, dataRaw);
739ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append(" THEN 2 ELSE " + Data.IS_PRIMARY + " END) AS " + SCORE);
740ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
741ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String[] PROJ_PRIMARY = new String[] {
742ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    DataColumns.CONCRETE_ID,
743ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Contacts.IS_RESTRICTED,
744ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    ContactsColumns.PACKAGE_ID,
745ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    scoreClause.toString(),
746ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            };
747ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
748ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_DATA_ID = 0;
749ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_IS_RESTRICTED = 1;
750ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_PACKAGE_ID = 2;
751ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_SCORE = 3;
752ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
753ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES, PROJ_PRIMARY,
754ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND " + DataColumns.MIMETYPE_ID
755ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                            + "=" + mimeId, null, null, null, SCORE);
756ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
757ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixOptimal) {
758ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
759ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colPackageId = null;
760ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
761ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID;
762ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colPackageId = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_PACKAGE_ID;
763ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
764ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID;
765ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colPackageId = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID;
766ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
767ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
768ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixOptimal told us that
769ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
770ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
771ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colPackageId);
772ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
773ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // When finding a new optimal primary, we only care about the
774ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // highest scoring value, regardless of source.
775ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (cursor.moveToFirst()) {
776ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final long newOptimal = cursor.getLong(COL_DATA_ID);
777ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final long newOptimalPackage = cursor.getLong(COL_PACKAGE_ID);
778ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
779ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (newOptimal != 0) {
780ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, newOptimal);
781ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
782ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (newOptimalPackage != 0) {
783ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colPackageId, newOptimalPackage);
784ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
785ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
786ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
787ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
788ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixFallback) {
789ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
790ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
791ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID;
792ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
793ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID;
794ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
795ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
796ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixFallback told us that
797ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
798ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
799ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
800ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // The best fallback value is the highest scoring data item that
801ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // hasn't been restricted.
802ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.moveToPosition(-1);
803ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                while (cursor.moveToNext()) {
804ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final boolean isRestricted = (cursor.getInt(COL_IS_RESTRICTED) == 1);
805ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (!isRestricted) {
806ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, cursor.getLong(COL_DATA_ID));
807ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        break;
808ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
809ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
810ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
811ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
812ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Push through any aggregate updates we have
813ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (values.size() > 0) {
814ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                db.update(Tables.AGGREGATES, values, AggregatesColumns.CONCRETE_ID + "=" + aggId,
815ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        null);
816ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
817ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
818ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
819ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return dataDeleted;
820ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
821ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
822ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
8234097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov     * Parse the supplied display name, but only if the incoming values do not already contain
8244097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov     * structured name parts.
8254097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov     */
8264097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private void parseStructuredName(ContentValues values) {
8274097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        final String fullName = values.getAsString(StructuredName.DISPLAY_NAME);
8284097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        if (TextUtils.isEmpty(fullName)
8294097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.PREFIX))
8304097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.GIVEN_NAME))
8314097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.MIDDLE_NAME))
8324097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.FAMILY_NAME))
8334097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.SUFFIX))) {
8344097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            return;
8354097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        }
8364097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
8374097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        NameSplitter.Name name = new NameSplitter.Name();
8384097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        mNameSplitter.split(name, fullName);
8394097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
8404097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.PREFIX, name.getPrefix());
8414097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.GIVEN_NAME, name.getGivenNames());
8424097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.MIDDLE_NAME, name.getMiddleName());
8434097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.FAMILY_NAME, name.getFamilyName());
8444097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.SUFFIX, name.getSuffix());
8454097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    }
8464097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
8474097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    /**
848ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
849ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
850ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private long insertGroup(ContentValues values, Account account) {
851ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
852ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
853ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ContentValues overriddenValues = new ContentValues(values);
854ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (!resolveAccount(overriddenValues, account)) {
855ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
856ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
857ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
858ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
859ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final String packageName = overriddenValues.getAsString(Groups.PACKAGE);
860ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        overriddenValues.put(Groups.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
861ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        overriddenValues.remove(Groups.PACKAGE);
862ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
863ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return db.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
864ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
865ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
866ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
8671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     * Inserts a presence update.
8681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
8691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private long insertPresence(ContentValues values) {
8701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
8711f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String handle = values.getAsString(Presence.IM_HANDLE);
8721f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String protocol = values.getAsString(Presence.IM_PROTOCOL);
8731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (TextUtils.isEmpty(handle) || TextUtils.isEmpty(protocol)) {
8741f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            throw new IllegalArgumentException("IM_PROTOCOL and IM_HANDLE are required");
8751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
8761f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
8771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // TODO: generalize to allow other providers to match against email
8781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        boolean matchEmail = GTALK_PROTOCOL_STRING.equals(protocol);
8791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
8801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String selection;
8811f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String[] selectionArgs;
8821f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (matchEmail) {
8831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selection = "(" + Clauses.WHERE_IM_MATCHES + ") OR (" + Clauses.WHERE_EMAIL_MATCHES + ")";
8841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle, handle };
8851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
8861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selection = Clauses.WHERE_IM_MATCHES;
8871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle };
8881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
8891f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
8901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long dataId = -1;
8911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long aggId = -1;
8921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
8931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
894ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES,
895ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Projections.PROJ_DATA_CONTACTS, selection, selectionArgs, null, null, null);
8961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
897ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                dataId = cursor.getLong(Projections.COL_DATA_ID);
898ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                aggId = cursor.getLong(Projections.COL_AGGREGATE_ID);
8991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
9001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
9011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
9021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
9031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
90431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
90531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
90631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
9071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
9081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
9091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.DATA_ID, dataId);
9101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.AGGREGATE_ID, aggId);
9111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
9121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Insert the presence update
9131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long presenceId = db.replace(Tables.PRESENCE, null, values);
9141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return presenceId;
9151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
9161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
9174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
9184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int delete(Uri uri, String selection, String[] selectionArgs) {
919508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
920508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
921508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
92235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
92335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().delete(db, selection, selectionArgs);
92435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
9256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_ID: {
9266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                long aggregateId = ContentUris.parseId(uri);
9276bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
9286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                // Remove references to the aggregate first
9296bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                ContentValues values = new ContentValues();
9306bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                values.putNull(Contacts.AGGREGATE_ID);
931b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                db.update(Tables.CONTACTS, values, Contacts.AGGREGATE_ID + "=" + aggregateId, null);
9326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
933b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return db.delete(Tables.AGGREGATES, BaseColumns._ID + "=" + aggregateId, null);
9346bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
9356bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
936508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case CONTACTS_ID: {
937508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long contactId = ContentUris.parseId(uri);
938b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                int contactsDeleted = db.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
939b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, Data.CONTACT_ID + "=" + contactId, null);
940508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                return contactsDeleted + dataDeleted;
941508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
942508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
943508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID: {
944508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
945ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return deleteData(dataId);
946ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
947ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
948ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
949ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
950ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final long groupMembershipMimetypeId = mOpenHelper
951ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
952ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int groupsDeleted = db.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
953ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
954ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
955ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupId, null);
956ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
957ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return groupsDeleted + dataDeleted;
958508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
959508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
9601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
9611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return db.delete(Tables.PRESENCE, null, null);
9621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
9631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
964508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            default:
965508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                throw new UnsupportedOperationException("Unknown uri: " + uri);
966508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
9674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
9684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
969f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private static Account readAccountFromQueryParams(Uri uri) {
970035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String name = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
971035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String type = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
972f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(type)) {
973f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            return null;
974f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        }
975f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        return new Account(name, type);
976f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    }
977f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
978ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
9794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
9804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
98100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
98235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
98300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
98400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
98500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
98635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
98735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().update(db, values, selection, selectionArgs);
98835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
989c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            // TODO(emillar): We will want to disallow editing the aggregates table at some point.
99000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            case AGGREGATES: {
99100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                count = db.update(Tables.AGGREGATES, values, selection, selectionArgs);
99200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
99300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
99400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
99500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            case AGGREGATES_ID: {
996d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                count = updateAggregateData(db, ContentUris.parseId(uri), values);
997c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
998c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
999c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1000c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            case DATA_ID: {
1001c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                boolean containsIsSuperPrimary = values.containsKey(Data.IS_SUPER_PRIMARY);
1002c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                boolean containsIsPrimary = values.containsKey(Data.IS_PRIMARY);
1003c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                final long id = ContentUris.parseId(uri);
1004c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1005c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                // Remove primary or super primary values being set to 0. This is disallowed by the
1006c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                // content provider.
10078b341f8ea85257c5f7103863405e0273921e16bcEvan Millar                if (containsIsSuperPrimary && values.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
1008c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    containsIsSuperPrimary = false;
1009c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_SUPER_PRIMARY);
1010c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
10118b341f8ea85257c5f7103863405e0273921e16bcEvan Millar                if (containsIsPrimary && values.getAsInteger(Data.IS_PRIMARY) == 0) {
1012c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    containsIsPrimary = false;
1013c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_PRIMARY);
1014c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
1015c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1016c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                if (containsIsSuperPrimary) {
1017c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsSuperPrimary(id);
1018c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsPrimary(id);
1019c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1020c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    // Now that we've taken care of setting these, remove them from "values".
1021c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_SUPER_PRIMARY);
1022c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    if (containsIsPrimary) {
1023c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                        values.remove(Data.IS_PRIMARY);
1024c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    }
1025c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                } else if (containsIsPrimary) {
1026c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsPrimary(id);
1027c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1028c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    // Now that we've taken care of setting this, remove it from "values".
1029c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_PRIMARY);
1030c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
1031c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1032c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                if (values.size() > 0) {
1033c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    String selectionWithId = (Data._ID + " = " + ContentUris.parseId(uri) + " ")
1034f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                            + (selection == null ? "" : " AND " + selection);
1035c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    count = db.update(Tables.DATA, values, selectionWithId, selectionArgs);
1036c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
103700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
103800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
10397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS: {
10417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.CONTACTS, values, selection, selectionArgs);
10427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
10437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
10447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS_ID: {
10467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selectionWithId = (Contacts._ID + " = " + ContentUris.parseId(uri) + " ")
10477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        + (selection == null ? "" : " AND " + selection);
10487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.CONTACTS, values, selectionWithId, selectionArgs);
10497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                Log.i(TAG, "Selection is: " + selectionWithId);
10507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
10517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
10527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case DATA: {
10547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.DATA, values, selection, selectionArgs);
10557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
10567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
10577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1058ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1059ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selection, selectionArgs);
1060ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
1061ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1062ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1063ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1064ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1065ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1066ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String selectionWithId = (Groups._ID + "=" + groupId + " ")
1067ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + (selection == null ? "" : " AND " + selection);
1068ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selectionWithId, selectionArgs);
1069ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1070ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // If changing visibility, then update aggregates
1071ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (values.containsKey(Groups.GROUP_VISIBLE)) {
1072ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    mOpenHelper.updateAllVisible();
1073ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1074ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1075ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1076ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1077ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1078127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
1079127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                count = updateAggregationException(db, values);
1080b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
1081b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1082b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1083619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case RESTRICTION_EXCEPTIONS: {
1084619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // Enforce required fields
1085619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                boolean hasFields = values.containsKey(RestrictionExceptions.PACKAGE_PROVIDER)
1086619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        && values.containsKey(RestrictionExceptions.PACKAGE_CLIENT)
1087619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        && values.containsKey(RestrictionExceptions.ALLOW_ACCESS);
1088619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                if (!hasFields) {
1089619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    throw new IllegalArgumentException("PACKAGE_PROVIDER, PACKAGE_CLIENT, and"
1090619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                            + "ALLOW_ACCESS are all required fields");
1091619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1092619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1093619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final String packageProvider = values
1094619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        .getAsString(RestrictionExceptions.PACKAGE_PROVIDER);
1095619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final boolean allowAccess = (values
1096619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        .getAsInteger(RestrictionExceptions.ALLOW_ACCESS) == 1);
1097619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1098619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final Context context = getContext();
1099619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final PackageManager pm = context.getPackageManager();
1100619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1101619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // Enforce that caller has authority over the requested package
1102619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: move back to Binder.getCallingUid() when we can stub-out test suite
1103619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final int callingUid = OpenHelper
1104619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        .getUidForPackageName(pm, context.getPackageName());
1105619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final String[] ownedPackages = pm.getPackagesForUid(callingUid);
1106619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                if (!isContained(ownedPackages, packageProvider)) {
1107619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    throw new RuntimeException(
1108619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                            "Requested PACKAGE_PROVIDER doesn't belong to calling UID.");
1109619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1110619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1111619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // Add or remove exception using exception helper
1112619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                if (allowAccess) {
1113619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    mOpenHelper.addRestrictionException(context, values);
1114619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                } else {
1115619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    mOpenHelper.removeRestrictionException(context, values);
1116619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1117619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1118619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1119619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1120619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
11217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
11227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
112300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
112400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
112500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        if (count > 0) {
112600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            getContext().getContentResolver().notifyChange(uri, null);
112700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
112800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
11294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
11304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1131d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    private int updateAggregateData(SQLiteDatabase db, long aggregateId, ContentValues values) {
1132d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1133d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // First update all constituent contacts
1134d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        ContentValues optionValues = new ContentValues(3);
1135d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (values.containsKey(Aggregates.CUSTOM_RINGTONE)) {
1136d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            optionValues.put(ContactOptionsColumns.CUSTOM_RINGTONE,
1137d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                    values.getAsString(Aggregates.CUSTOM_RINGTONE));
1138d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1139d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (values.containsKey(Aggregates.SEND_TO_VOICEMAIL)) {
1140d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            optionValues.put(ContactOptionsColumns.SEND_TO_VOICEMAIL,
1141d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                    values.getAsBoolean(Aggregates.SEND_TO_VOICEMAIL));
1142d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1143d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1144d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
1145d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (optionValues.size() == 0) {
1146d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
1147d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1148d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1149ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor c = db.query(Tables.CONTACTS, Projections.PROJ_CONTACTS, Contacts.AGGREGATE_ID + "="
1150d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                + aggregateId, null, null, null, null);
1151d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        try {
1152d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            while (c.moveToNext()) {
1153ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long contactId = c.getLong(Projections.COL_CONTACT_ID);
1154d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1155d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                optionValues.put(ContactOptionsColumns._ID, contactId);
1156d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                db.replace(Tables.CONTACT_OPTIONS, null, optionValues);
1157d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            }
1158d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        } finally {
1159d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            c.close();
1160d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1161d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1162d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Now update the aggregate itself.  Ignore all supplied fields except rington and
1163d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // send_to_voicemail
1164d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        optionValues.clear();
1165d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (values.containsKey(Aggregates.CUSTOM_RINGTONE)) {
1166d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            optionValues.put(Aggregates.CUSTOM_RINGTONE,
1167d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                    values.getAsString(Aggregates.CUSTOM_RINGTONE));
1168d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1169d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (values.containsKey(Aggregates.SEND_TO_VOICEMAIL)) {
1170d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            optionValues.put(Aggregates.SEND_TO_VOICEMAIL,
1171d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                    values.getAsBoolean(Aggregates.SEND_TO_VOICEMAIL));
1172d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1173d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1174d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        return db.update(Tables.AGGREGATES, optionValues, Aggregates._ID + "=" + aggregateId, null);
1175d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
1176d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1177127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private static class ContactPair {
1178127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        final long contactId1;
1179127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        final long contactId2;
1180127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1181127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        /**
1182127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         * Constructor that ensures that this.contactId1 &lt; this.contactId2
1183127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         */
1184127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        public ContactPair(long contactId1, long contactId2) {
1185127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (contactId1 < contactId2) {
1186127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId1 = contactId1;
1187127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId2 = contactId2;
1188127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
1189127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId2 = contactId1;
1190127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId1 = contactId2;
1191127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1192127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1193127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    }
119480c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1195127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
1196127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
1197127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long aggregateId = values.getAsInteger(AggregationExceptions.AGGREGATE_ID);
1198127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long contactId = values.getAsInteger(AggregationExceptions.CONTACT_ID);
119980c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1200127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // First, we build a list of contactID-contactID pairs for the given aggregate and contact.
1201127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ArrayList<ContactPair> pairs = new ArrayList<ContactPair>();
1202ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor c = db.query(Tables.CONTACTS, Projections.PROJ_CONTACTS,
1203127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                Contacts.AGGREGATE_ID + "=" + aggregateId,
1204127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                null, null, null, null);
1205127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        try {
1206127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            while (c.moveToNext()) {
1207ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long aggregatedContactId = c.getLong(Projections.COL_CONTACT_ID);
1208e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                if (aggregatedContactId != contactId) {
1209e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                    pairs.add(new ContactPair(aggregatedContactId, contactId));
1210e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                }
1211b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1212b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        } finally {
1213b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            c.close();
1214b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
1215127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1216127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // Now we iterate through all contact pairs to see if we need to insert/delete/update
1217127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // the corresponding exception
1218127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ContentValues exceptionValues = new ContentValues(3);
1219127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
1220127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        for (ContactPair pair : pairs) {
1221127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            final String whereClause =
1222127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    AggregationExceptionColumns.CONTACT_ID1 + "=" + pair.contactId1 + " AND "
1223127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    + AggregationExceptionColumns.CONTACT_ID2 + "=" + pair.contactId2;
1224127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
1225127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.delete(Tables.AGGREGATION_EXCEPTIONS, whereClause, null);
1226127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
1227127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.CONTACT_ID1, pair.contactId1);
1228127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.CONTACT_ID2, pair.contactId2);
1229127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
1230127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                        exceptionValues);
1231127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1232127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1233127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1234127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        mContactAggregator.markContactForAggregation(contactId);
1235127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        mContactAggregator.aggregateContact(contactId);
12367a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC
12377a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov                || exceptionType == AggregationExceptions.TYPE_KEEP_OUT) {
12387a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov            mContactAggregator.updateAggregateData(aggregateId);
12397a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov        }
1240127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1241127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
1242127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
1243127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
1244b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
1245b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1246619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1247619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list.
1248619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1249619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private boolean isContained(String[] array, String value) {
1250bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array != null) {
1251bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            for (String test : array) {
1252bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                if (value.equals(test)) {
1253bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                    return true;
1254bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                }
1255619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1256619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1257619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return false;
1258619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1259619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1260619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1261619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list, and add to the
1262619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * array if the value doesn't already appear.
1263619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1264619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private String[] assertContained(String[] array, String value) {
1265bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array == null) {
1266bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            array = new String[] {value};
1267bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        } else if (!isContained(array, value)) {
1268619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            String[] newArray = new String[array.length + 1];
1269619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            System.arraycopy(array, 0, newArray, 0, array.length);
1270619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            newArray[array.length] = value;
1271619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            array = newArray;
1272619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1273619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return array;
1274619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1275619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
12764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
12774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
12784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
12794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
128035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1281d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
12821f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
12831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String limit = null;
1284bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        String aggregateIdColName = Tables.AGGREGATES + "." + Aggregates._ID;
12854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1286619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
1287619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
1288a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
12894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
129035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
129135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().query(db, projection, selection,  selectionArgs,
129235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
129335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
12946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: {
1295b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                qb.setTables(Tables.AGGREGATES);
1296619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1297619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesProjectionMap);
1298619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setProjectionMap(sAggregatesProjectionMap);
1299619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1300619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1301619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1302619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case AGGREGATES_ID: {
1303619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = ContentUris.parseId(uri);
1304619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setTables(Tables.AGGREGATES);
1305ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND ");
1306619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1307619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesProjectionMap);
13086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                qb.setProjectionMap(sAggregatesProjectionMap);
13096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
13106bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
13116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
13121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case AGGREGATES_SUMMARY: {
1313619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
13141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1315619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1316619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1317619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
13181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1319bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
13201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
13211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
13221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
13231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case AGGREGATES_SUMMARY_ID: {
1324619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
13251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                long aggId = ContentUris.parseId(uri);
13261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1327ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND ");
1328619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1329619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1330619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
13311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1332bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
13331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
13341f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
13351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1336ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case AGGREGATES_SUMMARY_FILTER: {
1337619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1338ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1339ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1340ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
1341ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1342ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
1343bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
1344ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1345ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1346ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1347d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            case AGGREGATES_SUMMARY_STREQUENT_FILTER:
1348d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            case AGGREGATES_SUMMARY_STREQUENT: {
1349d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the first query for starred
1350d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1351d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1352d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if (match == AGGREGATES_SUMMARY_STREQUENT_FILTER
1353d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
1354d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1355d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1356d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String starredQuery = qb.buildQuery(projection, Aggregates.STARRED + "=1",
1357bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                        null, aggregateIdColName, null, null,
1358d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        null /* limit */);
1359d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1360d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
1361d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
1362d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1363d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1364d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if (match == AGGREGATES_SUMMARY_STREQUENT_FILTER
1365d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
1366d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1367d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1368d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String frequentQuery = qb.buildQuery(projection,
1369d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        Aggregates.TIMES_CONTACTED + " > 0 AND (" + Aggregates.STARRED
1370d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        + " = 0 OR " + Aggregates.STARRED + " IS NULL)",
1371bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                        null, aggregateIdColName, null, null, null);
1372d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1373d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
1374d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
1375d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
1376d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                Cursor c = db.rawQueryWithFactory(null, query, null,
1377d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1378d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1379d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if ((c != null) && !isTemporary()) {
1380d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
1381d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
1382d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1383d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
1384d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
1385d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1386b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            case AGGREGATES_SUMMARY_GROUP: {
1387b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1388b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                applyAggregateRestrictionExceptions(qb);
1389b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1390b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
1391b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1392b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
1393b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                    qb.appendWhere(" AND " + sAggregatesInGroupSelect);
1394b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                    selectionArgs = appendGroupArg(selectionArgs, uri.getLastPathSegment());
1395b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
1396b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                groupBy = aggregateIdColName;
1397b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
1398b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
1399b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
14006bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_DATA: {
1401619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = Long.parseLong(uri.getPathSegments().get(1));
1402ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1403de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1404619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.appendWhere(Contacts.AGGREGATE_ID + "=" + aggId + " AND ");
1405619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
14066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
14076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
140800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
1409ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
1410ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1411ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1412ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
1413ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
1414ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    qb.appendWhere(" AND " + buildAggregateLookupWhereClause(
1415ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                            uri.getLastPathSegment()));
1416ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
1417ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1418ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1419ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1420ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES: {
1421ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1422ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1423ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = \"" + Phone.CONTENT_ITEM_TYPE + "\"");
1424ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1425ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1426ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1427ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
1428ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1429ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1430ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = \"" + Postal.CONTENT_ITEM_TYPE + "\"");
1431ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1432ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1433ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
14344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS: {
1435035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.setTables(Tables.CONTACTS_JOIN_PACKAGES);
14364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sContactsProjectionMap);
1437619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyContactsRestrictionExceptions(qb);
14384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
14394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
14404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
14414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS_ID: {
1442619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long contactId = ContentUris.parseId(uri);
1443035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.setTables(Tables.CONTACTS_JOIN_PACKAGES);
14444f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sContactsProjectionMap);
1445ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(ContactsColumns.CONCRETE_ID + "=" + contactId + " AND ");
1446619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyContactsRestrictionExceptions(qb);
14474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
14484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
14494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1450a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS_DATA: {
1451619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long contactId = Long.parseLong(uri.getPathSegments().get(1));
1452ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES);
14537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                qb.setProjectionMap(sDataContactsProjectionMap);
1454619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.appendWhere(Data.CONTACT_ID + "=" + contactId + " AND ");
1455619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
1456a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1457a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1458a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
145928ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey            case CONTACTS_FILTER_EMAIL: {
1460619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1461ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1462e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.setProjectionMap(sDataContactsProjectionMap);
14635d0f923eb4c5351ebf323cc6f19c82acff98693eJeff Sharkey                qb.appendWhere(Data.MIMETYPE + "='" + CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'");
1464e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhere(" AND " + CommonDataKinds.Email.DATA + "=");
1465e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhereEscapeString(uri.getPathSegments().get(2));
1466e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
1467e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
1468e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
1469e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
1470035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                final String accountName = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
1471035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                final String accountType = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
1472343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                if (!TextUtils.isEmpty(accountName)) {
1473035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                    qb.appendWhere(Contacts.ACCOUNT_NAME + "="
1474035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountName) + " AND "
1475035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + Contacts.ACCOUNT_TYPE + "="
1476035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountType) + " AND ");
1477343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                }
1478ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES);
1479e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.setProjectionMap(sDataProjectionMap);
1480619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
1481e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
1482e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
1483e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
14844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
1485ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES);
14864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sDataProjectionMap);
1487ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(DataColumns.CONCRETE_ID + "=" + ContentUris.parseId(uri) + " AND ");
1488619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
14894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
14904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
14914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1492a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
1493619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1494a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
1495a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
1496a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
1497e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                    sortOrder = Data.CONTACT_ID;
1498a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
1499a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1500a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                final String number = uri.getLastPathSegment();
1501bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov                OpenHelper.buildPhoneLookupQuery(qb, number);
1502a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                qb.setProjectionMap(sDataContactsProjectionMap);
1503a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1504a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1505a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1506ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1507ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
1508ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
1509ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1510ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1511ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1512ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1513ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1514ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
1515ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
1516ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(GroupsColumns.CONCRETE_ID + "=" + groupId);
1517ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1518ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1519ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1520ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
1521ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES_DATA_CONTACTS_AGGREGATES);
1522ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
1523ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                groupBy = GroupsColumns.CONCRETE_ID;
1524ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1525ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1526ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1527b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
1528127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS_JOIN_CONTACTS);
1529b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
1530b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
1531b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1532b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
153331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
153431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                long aggregateId = Long.parseLong(uri.getPathSegments().get(1));
153531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final String maxSuggestionsParam =
153631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        uri.getQueryParameter(AggregationSuggestions.MAX_SUGGESTIONS);
153731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
153831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
153931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                if (maxSuggestionsParam != null) {
154031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(maxSuggestionsParam);
154131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
154231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
154331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
154431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
154531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(aggregateId, projection,
154631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        sAggregatesProjectionMap, maxSuggestions);
154731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
154831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1549619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case RESTRICTION_EXCEPTIONS: {
1550619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setTables(Tables.RESTRICTION_EXCEPTIONS);
1551619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setProjectionMap(sRestrictionExceptionsProjectionMap);
1552619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1553619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1554619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
15554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
15564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                throw new UnsupportedOperationException("Unknown uri: " + uri);
15574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
15584f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
15594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Perform the query and set the notification uri
15601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final Cursor c = qb.query(db, projection, selection, selectionArgs,
1561bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy, null, sortOrder, limit);
15624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
15634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
15644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
15654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
15664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
15674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
15687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
1569619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Restrict selection of {@link Aggregates} to only public ones, or those
1570619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * the caller has been granted a {@link RestrictionExceptions} to.
1571619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1572619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyAggregateRestrictionExceptions(SQLiteQueryBuilder qb) {
1573619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = OpenHelper.getUidForPackageName(getContext().getPackageManager(),
1574619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                getContext().getPackageName());
1575619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1576619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere("(" + AggregatesColumns.SINGLE_RESTRICTED_PACKAGE_ID + " IS NULL");
1577619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String exceptionClause = mOpenHelper.getRestrictionExceptionClause(clientUid,
1578619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.SINGLE_RESTRICTED_PACKAGE_ID);
1579619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (exceptionClause != null) {
1580619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            qb.appendWhere(" OR (" + exceptionClause + ")");
1581619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1582619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere(")");
1583619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1584619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1585619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1586619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the calling process, and
1587619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * add projections to correctly select {@link Aggregates#PRIMARY_PHONE_ID}
1588619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * and {@link Aggregates#PRIMARY_EMAIL_ID}.
1589619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1590619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyAggregatePrimaryRestrictionExceptions(HashMap<String, String> projection) {
1591619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: move back to Binder.getCallingUid() when we can stub-out test suite
1592619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = OpenHelper.getUidForPackageName(getContext().getPackageManager(),
1593619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                getContext().getPackageName());
1594619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1595619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String projectionPhone = "(CASE WHEN "
1596619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + mOpenHelper.getRestrictionExceptionClause(clientUid,
1597619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        AggregatesColumns.OPTIMAL_PRIMARY_PHONE_PACKAGE_ID) + " THEN "
1598619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID + " ELSE "
1599619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID + " END) AS "
1600619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + Aggregates.PRIMARY_PHONE_ID;
1601619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.remove(Aggregates.PRIMARY_PHONE_ID);
1602619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.put(Aggregates.PRIMARY_PHONE_ID, projectionPhone);
1603619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1604619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String projectionEmail = "(CASE WHEN "
1605619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + mOpenHelper.getRestrictionExceptionClause(clientUid,
1606619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID) + " THEN "
1607619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID + " ELSE "
1608619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID + " END) AS "
1609619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + Aggregates.PRIMARY_EMAIL_ID;
1610619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.remove(Aggregates.PRIMARY_EMAIL_ID);
1611619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.put(Aggregates.PRIMARY_EMAIL_ID, projectionEmail);
1612619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1613619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1614619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1615619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
1616619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
1617619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
1618619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1619619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyContactsRestrictionExceptions(SQLiteQueryBuilder qb) {
1620619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: move back to Binder.getCallingUid() when we can stub-out test suite
1621619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = OpenHelper.getUidForPackageName(getContext().getPackageManager(),
1622619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                getContext().getPackageName());
1623619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1624619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere("(" + Contacts.IS_RESTRICTED + "=0");
1625619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String exceptionClause = mOpenHelper.getRestrictionExceptionClause(clientUid,
1626619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                ContactsColumns.PACKAGE_ID);
1627619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (exceptionClause != null) {
1628619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            qb.appendWhere(" OR (" + exceptionClause + ")");
1629619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1630619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere(")");
1631619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1632619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1633619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1634619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
1635619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
1636619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
1637619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1638619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyDataRestrictionExceptions(SQLiteQueryBuilder qb) {
1639619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        applyContactsRestrictionExceptions(qb);
1640619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1641619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1642619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
16437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
16447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
16457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
16467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    private static class ContactsEntityIterator implements EntityIterator {
16477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
16487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
16497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
16517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data1",
16527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data2",
16537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data3",
16547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data4",
16557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data5",
16567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data6",
16577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data7",
16587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data8",
16597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data9",
16607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data10"};
16617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] PROJECTION = new String[]{
1663035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_NAME,
1664035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_TYPE,
1665035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.SOURCE_ID,
1666035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.VERSION,
1667035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.DIRTY,
1668035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data._ID,
1669035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.MIMETYPE,
1670035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA1,
1671035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA2,
1672035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA3,
1673035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA4,
1674035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA5,
1675035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA6,
1676035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA7,
1677035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA8,
1678035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA9,
1679035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA10,
1680035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.CONTACT_ID,
1681035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.IS_PRIMARY,
1682035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA_VERSION};
1683035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
1684035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
1685035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
1686035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
1687035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
1688035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
1689035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
1690035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_MIMETYPE = 6;
1691035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA1 = 7;
1692035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_CONTACT_ID = 17;
1693035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_IS_PRIMARY = 18;
1694035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_VERSION = 19;
16957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public ContactsEntityIterator(ContactsProvider2 provider, String contactsIdString, Uri uri,
16977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
16987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
16997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final String updatedSortOrder = (sortOrder == null)
17017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    ? Contacts.Data.CONTACT_ID
17027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    : (Contacts.Data.CONTACT_ID + "," + sortOrder);
17037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
17057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1706ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES);
1707035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            qb.setProjectionMap(sDataContactsProjectionMap);
17087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
17097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                qb.appendWhere(Data.CONTACT_ID + "=" + contactsIdString);
17107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
1711035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final String accountName = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
1712035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final String accountType = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
1713035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (!TextUtils.isEmpty(accountName)) {
1714035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.appendWhere(Contacts.ACCOUNT_NAME + "="
1715035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
1716035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + Contacts.ACCOUNT_TYPE + "="
1717035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
1718035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
17197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
17207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    null, null, updatedSortOrder);
17217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
17227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
17237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
17257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
17267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
17277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
17287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
17297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
17307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
17317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
17337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
17347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
17357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
17367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
17387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
17397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
17417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
17427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
17437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
17447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
17457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
17467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
17477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
17497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final long contactId = c.getLong(COLUMN_CONTACT_ID);
17517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
17537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
1754035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            contactValues.put(Contacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
1755035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            contactValues.put(Contacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
17567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts._ID, contactId);
17577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts.DIRTY, c.getLong(COLUMN_DIRTY));
1758f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            contactValues.put(Contacts.VERSION, c.getLong(COLUMN_VERSION));
17597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
17607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
17617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
17637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
17647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                if (contactId != c.getLong(COLUMN_CONTACT_ID)) {
17657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
17667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
17677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
17687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
17697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                dataValues.put(Contacts.Data._ID, c.getString(COLUMN_DATA_ID));
1770f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
1771f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.IS_PRIMARY, c.getString(COLUMN_IS_PRIMARY));
1772f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
17737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                for (int i = 0; i < 10; i++) {
17747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
17757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
17767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
17777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
17787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
17797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
17807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
17817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
17827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
17837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
17847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
17857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
17867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
17877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
17887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
17897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
17907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
17927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
17937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
17947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1795a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
17967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
17977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
17987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
17997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
18007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS:
18017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS_ID:
18027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
18037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                if (match == CONTACTS_ID) {
18047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
18057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
18067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                return new ContactsEntityIterator(this, contactsIdString,
18087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        uri, selection, selectionArgs, sortOrder);
18097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
18107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
18117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
18127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
18137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
18154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
1816a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
18174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
18186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: return Aggregates.CONTENT_TYPE;
18196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_ID: return Aggregates.CONTENT_ITEM_TYPE;
18204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS: return Contacts.CONTENT_TYPE;
18214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS_ID: return Contacts.CONTENT_ITEM_TYPE;
1822508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
18236bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
1824508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
1825b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return mOpenHelper.getDataMimeType(dataId);
182631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: return AggregationExceptions.CONTENT_TYPE;
182731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTION_ID: return AggregationExceptions.CONTENT_ITEM_TYPE;
182831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: return Aggregates.CONTENT_TYPE;
18294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
1830a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        throw new UnsupportedOperationException("Unknown uri: " + uri);
18314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
18327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1833b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    @Override
18347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
18357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            throws OperationApplicationException {
18367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
18387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        db.beginTransaction();
18397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        try {
18407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentProviderResult[] results = super.applyBatch(operations);
18417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.setTransactionSuccessful();
18427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return results;
18437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        } finally {
18447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.endTransaction();
18457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
18467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
1847c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1848c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
1849c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
1850c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
1851c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
1852c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
1853c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
1854c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsPrimary(long dataId) {
1855c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
1856c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(2, dataId);
1857c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(3, dataId);
1858c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
1859c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
1860c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1861c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
1862c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
1863c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
1864c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
1865c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
1866c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
1867c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsSuperPrimary(long dataId) {
1868c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
1869c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(2, dataId);
1870c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(3, dataId);
1871c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
1872619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1873619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Find the parent aggregate and package for this new primary
1874619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1875619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1876619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        long aggId = -1;
1877619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        long packageId = -1;
1878619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isRestricted = false;
1879619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        String mimeType = null;
1880619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1881619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        Cursor cursor = null;
1882619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        try {
1883ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES,
1884ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Projections.PROJ_DATA_CONTACTS, DataColumns.CONCRETE_ID + "=" + dataId, null,
1885ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    null, null, null);
1886619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor.moveToFirst()) {
1887ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                aggId = cursor.getLong(Projections.COL_AGGREGATE_ID);
1888ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                packageId = cursor.getLong(Projections.COL_PACKAGE_ID);
1889ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                isRestricted = (cursor.getInt(Projections.COL_IS_RESTRICTED) == 1);
1890ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mimeType = cursor.getString(Projections.COL_MIMETYPE);
1891619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1892619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } finally {
1893619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor != null) {
1894619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                cursor.close();
1895619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1896619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1897619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1898619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Bypass aggregate update if no parent found, or if we don't keep track
1899619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // of super-primary for this mimetype.
1900d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (aggId == -1) {
1901d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return;
1902d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1903619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1904619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isPhone = CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimeType);
1905619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isEmail = CommonDataKinds.Email.CONTENT_ITEM_TYPE.equals(mimeType);
1906619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1907619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Record this value as the new primary for the parent aggregate
1908619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final ContentValues values = new ContentValues();
1909619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (isPhone) {
1910619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID, dataId);
1911619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_PHONE_PACKAGE_ID, packageId);
1912619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (isEmail) {
1913619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID, dataId);
1914619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID, packageId);
1915619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1916619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1917619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // If this data is unrestricted, then also set as fallback
1918619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (!isRestricted && isPhone) {
1919619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID, dataId);
1920619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (!isRestricted && isEmail) {
1921619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID, dataId);
1922619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1923619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1924619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Push update into aggregates table, if needed
1925619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (values.size() > 0) {
1926619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            db.update(Tables.AGGREGATES, values, Aggregates._ID + "=" + aggId, null);
1927619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1928619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1929c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
1930ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1931ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private String buildAggregateLookupWhereClause(String filterParam) {
1932ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        StringBuilder filter = new StringBuilder();
1933ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Tables.AGGREGATES);
1934ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(".");
1935ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Aggregates._ID);
1936ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" IN (SELECT ");
1937ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Contacts.AGGREGATE_ID);
1938ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" FROM ");
1939ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Tables.CONTACTS);
1940ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" WHERE ");
1941ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Contacts._ID);
1942d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append(" IN (SELECT  contact_id FROM name_lookup WHERE normalized_name GLOB '");
1943ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // NOTE: Query parameters won't work here since the SQL compiler
1944ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // needs to parse the actual string to know that it can use the
1945ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // index to do a prefix scan.
1946d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append(NameNormalizer.normalize(filterParam) + "*");
1947d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append("'))");
1948ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        return filter.toString();
1949ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
1950ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1951b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private String[] appendGroupArg(String[] selectionArgs, String arg) {
1952b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
1953b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
1954b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
1955b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
1956b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
1957b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            System.arraycopy(selectionArgs, 0, newSelectionArgs, 0, selectionArgs.length);
1958b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            newSelectionArgs[newLength - 1] = arg;
1959b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
1960b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
1961b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
19624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
1963