LegacyApiSupport.java revision 2d2ec88b7af615b2f05e987da45425be9cace1ba
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16package com.android.providers.contacts;
17
18import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
19import com.android.providers.contacts.ContactsDatabaseHelper.ExtensionsColumns;
20import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
21import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
22import com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
23import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
24import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
25import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
26import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
27import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
28
29import android.accounts.Account;
30import android.app.SearchManager;
31import android.content.ContentUris;
32import android.content.ContentValues;
33import android.content.Context;
34import android.content.UriMatcher;
35import android.database.Cursor;
36import android.database.DatabaseUtils;
37import android.database.SQLException;
38import android.database.sqlite.SQLiteDatabase;
39import android.database.sqlite.SQLiteDoneException;
40import android.database.sqlite.SQLiteQueryBuilder;
41import android.database.sqlite.SQLiteStatement;
42import android.net.Uri;
43import android.provider.BaseColumns;
44import android.provider.ContactsContract;
45import android.provider.Contacts.ContactMethods;
46import android.provider.Contacts.Extensions;
47import android.provider.Contacts.People;
48import android.provider.ContactsContract.Contacts;
49import android.provider.ContactsContract.Data;
50import android.provider.ContactsContract.Groups;
51import android.provider.ContactsContract.RawContacts;
52import android.provider.ContactsContract.Settings;
53import android.provider.ContactsContract.StatusUpdates;
54import android.provider.ContactsContract.CommonDataKinds.Email;
55import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
56import android.provider.ContactsContract.CommonDataKinds.Im;
57import android.provider.ContactsContract.CommonDataKinds.Note;
58import android.provider.ContactsContract.CommonDataKinds.Organization;
59import android.provider.ContactsContract.CommonDataKinds.Phone;
60import android.provider.ContactsContract.CommonDataKinds.Photo;
61import android.provider.ContactsContract.CommonDataKinds.StructuredName;
62import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
63import android.util.Log;
64
65import java.util.HashMap;
66import java.util.Locale;
67
68@SuppressWarnings("deprecation")
69public class LegacyApiSupport {
70
71    private static final String TAG = "ContactsProviderV1";
72
73    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
74
75    private static final int PEOPLE = 1;
76    private static final int PEOPLE_ID = 2;
77    private static final int PEOPLE_UPDATE_CONTACT_TIME = 3;
78    private static final int ORGANIZATIONS = 4;
79    private static final int ORGANIZATIONS_ID = 5;
80    private static final int PEOPLE_CONTACTMETHODS = 6;
81    private static final int PEOPLE_CONTACTMETHODS_ID = 7;
82    private static final int CONTACTMETHODS = 8;
83    private static final int CONTACTMETHODS_ID = 9;
84    private static final int PEOPLE_PHONES = 10;
85    private static final int PEOPLE_PHONES_ID = 11;
86    private static final int PHONES = 12;
87    private static final int PHONES_ID = 13;
88    private static final int EXTENSIONS = 14;
89    private static final int EXTENSIONS_ID = 15;
90    private static final int PEOPLE_EXTENSIONS = 16;
91    private static final int PEOPLE_EXTENSIONS_ID = 17;
92    private static final int GROUPS = 18;
93    private static final int GROUPS_ID = 19;
94    private static final int GROUPMEMBERSHIP = 20;
95    private static final int GROUPMEMBERSHIP_ID = 21;
96    private static final int PEOPLE_GROUPMEMBERSHIP = 22;
97    private static final int PEOPLE_GROUPMEMBERSHIP_ID = 23;
98    private static final int PEOPLE_PHOTO = 24;
99    private static final int PHOTOS = 25;
100    private static final int PHOTOS_ID = 26;
101    private static final int PEOPLE_FILTER = 29;
102    private static final int DELETED_PEOPLE = 30;
103    private static final int DELETED_GROUPS = 31;
104    private static final int SEARCH_SUGGESTIONS = 32;
105    private static final int SEARCH_SHORTCUT = 33;
106    private static final int PHONES_FILTER = 34;
107    private static final int LIVE_FOLDERS_PEOPLE = 35;
108    private static final int LIVE_FOLDERS_PEOPLE_GROUP_NAME = 36;
109    private static final int LIVE_FOLDERS_PEOPLE_WITH_PHONES = 37;
110    private static final int LIVE_FOLDERS_PEOPLE_FAVORITES = 38;
111    private static final int CONTACTMETHODS_EMAIL = 39;
112    private static final int GROUP_NAME_MEMBERS = 40;
113    private static final int GROUP_SYSTEM_ID_MEMBERS = 41;
114    private static final int PEOPLE_ORGANIZATIONS = 42;
115    private static final int PEOPLE_ORGANIZATIONS_ID = 43;
116    private static final int SETTINGS = 44;
117
118    private static final String PEOPLE_JOINS =
119            " LEFT OUTER JOIN data name ON (raw_contacts._id = name.raw_contact_id"
120            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = name.mimetype_id)"
121                    + "='" + StructuredName.CONTENT_ITEM_TYPE + "')"
122            + " LEFT OUTER JOIN data organization ON (raw_contacts._id = organization.raw_contact_id"
123            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = organization.mimetype_id)"
124                    + "='" + Organization.CONTENT_ITEM_TYPE + "' AND organization.is_primary)"
125            + " LEFT OUTER JOIN data email ON (raw_contacts._id = email.raw_contact_id"
126            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = email.mimetype_id)"
127                    + "='" + Email.CONTENT_ITEM_TYPE + "' AND email.is_primary)"
128            + " LEFT OUTER JOIN data note ON (raw_contacts._id = note.raw_contact_id"
129            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = note.mimetype_id)"
130                    + "='" + Note.CONTENT_ITEM_TYPE + "')"
131            + " LEFT OUTER JOIN data phone ON (raw_contacts._id = phone.raw_contact_id"
132            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = phone.mimetype_id)"
133                    + "='" + Phone.CONTENT_ITEM_TYPE + "' AND phone.is_primary)";
134
135    public static final String DATA_JOINS =
136            " JOIN mimetypes ON (mimetypes._id = data.mimetype_id)"
137            + " JOIN raw_contacts ON (raw_contacts._id = data.raw_contact_id)"
138            + PEOPLE_JOINS;
139
140    public static final String PRESENCE_JOINS =
141            " LEFT OUTER JOIN " + Tables.PRESENCE +
142            " ON (" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID + "=" +
143                    "(SELECT MAX(" + StatusUpdates.DATA_ID + ")" +
144                    " FROM " + Tables.PRESENCE +
145                    " WHERE people._id = " + PresenceColumns.RAW_CONTACT_ID + ")" +
146            " )";
147
148    private static final String PHONETIC_NAME_SQL = "trim(trim("
149            + "ifnull(name." + StructuredName.PHONETIC_GIVEN_NAME + ",' ')||' '||"
150            + "ifnull(name." + StructuredName.PHONETIC_MIDDLE_NAME + ",' '))||' '||"
151            + "ifnull(name." + StructuredName.PHONETIC_FAMILY_NAME + ",' ')) ";
152
153    private static final String CONTACT_METHOD_KIND_SQL =
154            "CAST ((CASE WHEN mimetype='" + Email.CONTENT_ITEM_TYPE + "'"
155                + " THEN " + android.provider.Contacts.KIND_EMAIL
156                + " ELSE"
157                    + " (CASE WHEN mimetype='" + Im.CONTENT_ITEM_TYPE +"'"
158                        + " THEN " + android.provider.Contacts.KIND_IM
159                        + " ELSE"
160                        + " (CASE WHEN mimetype='" + StructuredPostal.CONTENT_ITEM_TYPE + "'"
161                            + " THEN "  + android.provider.Contacts.KIND_POSTAL
162                            + " ELSE"
163                                + " NULL"
164                            + " END)"
165                        + " END)"
166                + " END) AS INTEGER)";
167
168    private static final String IM_PROTOCOL_SQL =
169            "(CASE WHEN " + StatusUpdates.PROTOCOL + "=" + Im.PROTOCOL_CUSTOM
170                + " THEN 'custom:'||" + StatusUpdates.CUSTOM_PROTOCOL
171                + " ELSE 'pre:'||" + StatusUpdates.PROTOCOL
172                + " END)";
173
174    private static String CONTACT_METHOD_DATA_SQL =
175            "(CASE WHEN " + Data.MIMETYPE + "='" + Im.CONTENT_ITEM_TYPE + "'"
176                + " THEN (CASE WHEN " + Tables.DATA + "." + Im.PROTOCOL + "=" + Im.PROTOCOL_CUSTOM
177                    + " THEN 'custom:'||" + Tables.DATA + "." + Im.CUSTOM_PROTOCOL
178                    + " ELSE 'pre:'||" + Tables.DATA + "." + Im.PROTOCOL
179                    + " END)"
180                + " ELSE " + Tables.DATA + "." + Email.DATA
181                + " END)";
182
183    private static final Uri LIVE_FOLDERS_CONTACTS_URI = Uri.withAppendedPath(
184            ContactsContract.AUTHORITY_URI, "live_folders/contacts");
185
186    private static final Uri LIVE_FOLDERS_CONTACTS_WITH_PHONES_URI = Uri.withAppendedPath(
187            ContactsContract.AUTHORITY_URI, "live_folders/contacts_with_phones");
188
189    private static final Uri LIVE_FOLDERS_CONTACTS_FAVORITES_URI = Uri.withAppendedPath(
190            ContactsContract.AUTHORITY_URI, "live_folders/favorites");
191
192    private static final String CONTACTS_UPDATE_LASTTIMECONTACTED =
193            "UPDATE " + Tables.CONTACTS +
194            " SET " + Contacts.LAST_TIME_CONTACTED + "=? " +
195            "WHERE " + Contacts._ID + "=?";
196    private static final String RAWCONTACTS_UPDATE_LASTTIMECONTACTED =
197            "UPDATE " + Tables.RAW_CONTACTS + " SET "
198            + RawContacts.LAST_TIME_CONTACTED + "=? WHERE "
199            + RawContacts._ID + "=?";
200
201    private String[] mSelectionArgs1 = new String[1];
202    private String[] mSelectionArgs2 = new String[2];
203
204    public interface LegacyTables {
205        public static final String PEOPLE = "view_v1_people";
206        public static final String PEOPLE_JOIN_PRESENCE = "view_v1_people people " + PRESENCE_JOINS;
207        public static final String GROUPS = "view_v1_groups";
208        public static final String ORGANIZATIONS = "view_v1_organizations";
209        public static final String CONTACT_METHODS = "view_v1_contact_methods";
210        public static final String PHONES = "view_v1_phones";
211        public static final String EXTENSIONS = "view_v1_extensions";
212        public static final String GROUP_MEMBERSHIP = "view_v1_group_membership";
213        public static final String PHOTOS = "view_v1_photos";
214        public static final String SETTINGS = "v1_settings";
215    }
216
217    private static final String[] ORGANIZATION_MIME_TYPES = new String[] {
218        Organization.CONTENT_ITEM_TYPE
219    };
220
221    private static final String[] CONTACT_METHOD_MIME_TYPES = new String[] {
222        Email.CONTENT_ITEM_TYPE,
223        Im.CONTENT_ITEM_TYPE,
224        StructuredPostal.CONTENT_ITEM_TYPE,
225    };
226
227    private static final String[] PHONE_MIME_TYPES = new String[] {
228        Phone.CONTENT_ITEM_TYPE
229    };
230
231    private static final String[] PHOTO_MIME_TYPES = new String[] {
232        Photo.CONTENT_ITEM_TYPE
233    };
234
235    private static final String[] GROUP_MEMBERSHIP_MIME_TYPES = new String[] {
236        GroupMembership.CONTENT_ITEM_TYPE
237    };
238
239    private static final String[] EXTENSION_MIME_TYPES = new String[] {
240        android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE
241    };
242
243    private interface IdQuery {
244        String[] COLUMNS = { BaseColumns._ID };
245
246        int _ID = 0;
247    }
248
249    /**
250     * A custom data row that is used to store legacy photo data fields no
251     * longer directly supported by the API.
252     */
253    private interface LegacyPhotoData {
254        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo_v1_extras";
255
256        public static final String PHOTO_DATA_ID = Data.DATA1;
257        public static final String LOCAL_VERSION = Data.DATA2;
258        public static final String DOWNLOAD_REQUIRED = Data.DATA3;
259        public static final String EXISTS_ON_SERVER = Data.DATA4;
260        public static final String SYNC_ERROR = Data.DATA5;
261    }
262
263    public static final String LEGACY_PHOTO_JOIN =
264            " LEFT OUTER JOIN data legacy_photo ON (raw_contacts._id = legacy_photo.raw_contact_id"
265            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = legacy_photo.mimetype_id)"
266                + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'"
267            + " AND " + DataColumns.CONCRETE_ID + " = legacy_photo." + LegacyPhotoData.PHOTO_DATA_ID
268            + ")";
269
270    private static final HashMap<String, String> sPeopleProjectionMap;
271    private static final HashMap<String, String> sOrganizationProjectionMap;
272    private static final HashMap<String, String> sContactMethodProjectionMap;
273    private static final HashMap<String, String> sPhoneProjectionMap;
274    private static final HashMap<String, String> sExtensionProjectionMap;
275    private static final HashMap<String, String> sGroupProjectionMap;
276    private static final HashMap<String, String> sGroupMembershipProjectionMap;
277    private static final HashMap<String, String> sPhotoProjectionMap;
278
279    static {
280
281        // Contacts URI matching table
282        UriMatcher matcher = sUriMatcher;
283
284        String authority = android.provider.Contacts.AUTHORITY;
285        matcher.addURI(authority, "extensions", EXTENSIONS);
286        matcher.addURI(authority, "extensions/#", EXTENSIONS_ID);
287        matcher.addURI(authority, "groups", GROUPS);
288        matcher.addURI(authority, "groups/#", GROUPS_ID);
289        matcher.addURI(authority, "groups/name/*/members", GROUP_NAME_MEMBERS);
290//        matcher.addURI(authority, "groups/name/*/members/filter/*",
291//                GROUP_NAME_MEMBERS_FILTER);
292        matcher.addURI(authority, "groups/system_id/*/members", GROUP_SYSTEM_ID_MEMBERS);
293//        matcher.addURI(authority, "groups/system_id/*/members/filter/*",
294//                GROUP_SYSTEM_ID_MEMBERS_FILTER);
295        matcher.addURI(authority, "groupmembership", GROUPMEMBERSHIP);
296        matcher.addURI(authority, "groupmembership/#", GROUPMEMBERSHIP_ID);
297//        matcher.addURI(authority, "groupmembershipraw", GROUPMEMBERSHIP_RAW);
298        matcher.addURI(authority, "people", PEOPLE);
299//        matcher.addURI(authority, "people/strequent", PEOPLE_STREQUENT);
300//        matcher.addURI(authority, "people/strequent/filter/*", PEOPLE_STREQUENT_FILTER);
301        matcher.addURI(authority, "people/filter/*", PEOPLE_FILTER);
302//        matcher.addURI(authority, "people/with_phones_filter/*",
303//                PEOPLE_WITH_PHONES_FILTER);
304//        matcher.addURI(authority, "people/with_email_or_im_filter/*",
305//                PEOPLE_WITH_EMAIL_OR_IM_FILTER);
306        matcher.addURI(authority, "people/#", PEOPLE_ID);
307        matcher.addURI(authority, "people/#/extensions", PEOPLE_EXTENSIONS);
308        matcher.addURI(authority, "people/#/extensions/#", PEOPLE_EXTENSIONS_ID);
309        matcher.addURI(authority, "people/#/phones", PEOPLE_PHONES);
310        matcher.addURI(authority, "people/#/phones/#", PEOPLE_PHONES_ID);
311//        matcher.addURI(authority, "people/#/phones_with_presence",
312//                PEOPLE_PHONES_WITH_PRESENCE);
313        matcher.addURI(authority, "people/#/photo", PEOPLE_PHOTO);
314//        matcher.addURI(authority, "people/#/photo/data", PEOPLE_PHOTO_DATA);
315        matcher.addURI(authority, "people/#/contact_methods", PEOPLE_CONTACTMETHODS);
316//        matcher.addURI(authority, "people/#/contact_methods_with_presence",
317//                PEOPLE_CONTACTMETHODS_WITH_PRESENCE);
318        matcher.addURI(authority, "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
319        matcher.addURI(authority, "people/#/organizations", PEOPLE_ORGANIZATIONS);
320        matcher.addURI(authority, "people/#/organizations/#", PEOPLE_ORGANIZATIONS_ID);
321        matcher.addURI(authority, "people/#/groupmembership", PEOPLE_GROUPMEMBERSHIP);
322        matcher.addURI(authority, "people/#/groupmembership/#", PEOPLE_GROUPMEMBERSHIP_ID);
323//        matcher.addURI(authority, "people/raw", PEOPLE_RAW);
324//        matcher.addURI(authority, "people/owner", PEOPLE_OWNER);
325        matcher.addURI(authority, "people/#/update_contact_time",
326                PEOPLE_UPDATE_CONTACT_TIME);
327        matcher.addURI(authority, "deleted_people", DELETED_PEOPLE);
328        matcher.addURI(authority, "deleted_groups", DELETED_GROUPS);
329        matcher.addURI(authority, "phones", PHONES);
330//        matcher.addURI(authority, "phones_with_presence", PHONES_WITH_PRESENCE);
331        matcher.addURI(authority, "phones/filter/*", PHONES_FILTER);
332//        matcher.addURI(authority, "phones/filter_name/*", PHONES_FILTER_NAME);
333//        matcher.addURI(authority, "phones/mobile_filter_name/*",
334//                PHONES_MOBILE_FILTER_NAME);
335        matcher.addURI(authority, "phones/#", PHONES_ID);
336        matcher.addURI(authority, "photos", PHOTOS);
337        matcher.addURI(authority, "photos/#", PHOTOS_ID);
338        matcher.addURI(authority, "contact_methods", CONTACTMETHODS);
339        matcher.addURI(authority, "contact_methods/email", CONTACTMETHODS_EMAIL);
340//        matcher.addURI(authority, "contact_methods/email/*", CONTACTMETHODS_EMAIL_FILTER);
341        matcher.addURI(authority, "contact_methods/#", CONTACTMETHODS_ID);
342//        matcher.addURI(authority, "contact_methods/with_presence",
343//                CONTACTMETHODS_WITH_PRESENCE);
344        matcher.addURI(authority, "organizations", ORGANIZATIONS);
345        matcher.addURI(authority, "organizations/#", ORGANIZATIONS_ID);
346//        matcher.addURI(authority, "voice_dialer_timestamp", VOICE_DIALER_TIMESTAMP);
347        matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY,
348                SEARCH_SUGGESTIONS);
349        matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
350                SEARCH_SUGGESTIONS);
351        matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
352                SEARCH_SHORTCUT);
353        matcher.addURI(authority, "settings", SETTINGS);
354
355        matcher.addURI(authority, "live_folders/people", LIVE_FOLDERS_PEOPLE);
356        matcher.addURI(authority, "live_folders/people/*",
357                LIVE_FOLDERS_PEOPLE_GROUP_NAME);
358        matcher.addURI(authority, "live_folders/people_with_phones",
359                LIVE_FOLDERS_PEOPLE_WITH_PHONES);
360        matcher.addURI(authority, "live_folders/favorites",
361                LIVE_FOLDERS_PEOPLE_FAVORITES);
362
363
364        HashMap<String, String> peopleProjectionMap = new HashMap<String, String>();
365        peopleProjectionMap.put(People.NAME, People.NAME);
366        peopleProjectionMap.put(People.DISPLAY_NAME, People.DISPLAY_NAME);
367        peopleProjectionMap.put(People.PHONETIC_NAME, People.PHONETIC_NAME);
368        peopleProjectionMap.put(People.NOTES, People.NOTES);
369        peopleProjectionMap.put(People.TIMES_CONTACTED, People.TIMES_CONTACTED);
370        peopleProjectionMap.put(People.LAST_TIME_CONTACTED, People.LAST_TIME_CONTACTED);
371        peopleProjectionMap.put(People.CUSTOM_RINGTONE, People.CUSTOM_RINGTONE);
372        peopleProjectionMap.put(People.SEND_TO_VOICEMAIL, People.SEND_TO_VOICEMAIL);
373        peopleProjectionMap.put(People.STARRED, People.STARRED);
374        peopleProjectionMap.put(People.PRIMARY_ORGANIZATION_ID, People.PRIMARY_ORGANIZATION_ID);
375        peopleProjectionMap.put(People.PRIMARY_EMAIL_ID, People.PRIMARY_EMAIL_ID);
376        peopleProjectionMap.put(People.PRIMARY_PHONE_ID, People.PRIMARY_PHONE_ID);
377
378        sPeopleProjectionMap = new HashMap<String, String>(peopleProjectionMap);
379        sPeopleProjectionMap.put(People._ID, People._ID);
380        sPeopleProjectionMap.put(People.NUMBER, People.NUMBER);
381        sPeopleProjectionMap.put(People.TYPE, People.TYPE);
382        sPeopleProjectionMap.put(People.LABEL, People.LABEL);
383        sPeopleProjectionMap.put(People.NUMBER_KEY, People.NUMBER_KEY);
384        sPeopleProjectionMap.put(People.IM_PROTOCOL, IM_PROTOCOL_SQL + " AS " + People.IM_PROTOCOL);
385        sPeopleProjectionMap.put(People.IM_HANDLE, People.IM_HANDLE);
386        sPeopleProjectionMap.put(People.IM_ACCOUNT, People.IM_ACCOUNT);
387        sPeopleProjectionMap.put(People.PRESENCE_STATUS, People.PRESENCE_STATUS);
388        sPeopleProjectionMap.put(People.PRESENCE_CUSTOM_STATUS,
389                "(SELECT " + StatusUpdates.STATUS +
390                " FROM " + Tables.STATUS_UPDATES +
391                " JOIN " + Tables.DATA +
392                "   ON(" + StatusUpdatesColumns.DATA_ID + "=" + DataColumns.CONCRETE_ID + ")" +
393                " WHERE " + DataColumns.CONCRETE_RAW_CONTACT_ID + "=people." + People._ID +
394                " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC " +
395                " LIMIT 1" +
396                ") AS " + People.PRESENCE_CUSTOM_STATUS);
397
398        sOrganizationProjectionMap = new HashMap<String, String>();
399        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations._ID,
400                android.provider.Contacts.Organizations._ID);
401        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.PERSON_ID,
402                android.provider.Contacts.Organizations.PERSON_ID);
403        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.ISPRIMARY,
404                android.provider.Contacts.Organizations.ISPRIMARY);
405        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.COMPANY,
406                android.provider.Contacts.Organizations.COMPANY);
407        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TYPE,
408                android.provider.Contacts.Organizations.TYPE);
409        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.LABEL,
410                android.provider.Contacts.Organizations.LABEL);
411        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TITLE,
412                android.provider.Contacts.Organizations.TITLE);
413
414        sContactMethodProjectionMap = new HashMap<String, String>(peopleProjectionMap);
415        sContactMethodProjectionMap.put(ContactMethods._ID, ContactMethods._ID);
416        sContactMethodProjectionMap.put(ContactMethods.PERSON_ID, ContactMethods.PERSON_ID);
417        sContactMethodProjectionMap.put(ContactMethods.KIND, ContactMethods.KIND);
418        sContactMethodProjectionMap.put(ContactMethods.ISPRIMARY, ContactMethods.ISPRIMARY);
419        sContactMethodProjectionMap.put(ContactMethods.TYPE, ContactMethods.TYPE);
420        sContactMethodProjectionMap.put(ContactMethods.DATA, ContactMethods.DATA);
421        sContactMethodProjectionMap.put(ContactMethods.LABEL, ContactMethods.LABEL);
422        sContactMethodProjectionMap.put(ContactMethods.AUX_DATA, ContactMethods.AUX_DATA);
423
424        sPhoneProjectionMap = new HashMap<String, String>(peopleProjectionMap);
425        sPhoneProjectionMap.put(android.provider.Contacts.Phones._ID,
426                android.provider.Contacts.Phones._ID);
427        sPhoneProjectionMap.put(android.provider.Contacts.Phones.PERSON_ID,
428                android.provider.Contacts.Phones.PERSON_ID);
429        sPhoneProjectionMap.put(android.provider.Contacts.Phones.ISPRIMARY,
430                android.provider.Contacts.Phones.ISPRIMARY);
431        sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER,
432                android.provider.Contacts.Phones.NUMBER);
433        sPhoneProjectionMap.put(android.provider.Contacts.Phones.TYPE,
434                android.provider.Contacts.Phones.TYPE);
435        sPhoneProjectionMap.put(android.provider.Contacts.Phones.LABEL,
436                android.provider.Contacts.Phones.LABEL);
437        sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER_KEY,
438                android.provider.Contacts.Phones.NUMBER_KEY);
439
440        sExtensionProjectionMap = new HashMap<String, String>();
441        sExtensionProjectionMap.put(android.provider.Contacts.Extensions._ID,
442                android.provider.Contacts.Extensions._ID);
443        sExtensionProjectionMap.put(android.provider.Contacts.Extensions.PERSON_ID,
444                android.provider.Contacts.Extensions.PERSON_ID);
445        sExtensionProjectionMap.put(android.provider.Contacts.Extensions.NAME,
446                android.provider.Contacts.Extensions.NAME);
447        sExtensionProjectionMap.put(android.provider.Contacts.Extensions.VALUE,
448                android.provider.Contacts.Extensions.VALUE);
449
450        sGroupProjectionMap = new HashMap<String, String>();
451        sGroupProjectionMap.put(android.provider.Contacts.Groups._ID,
452                android.provider.Contacts.Groups._ID);
453        sGroupProjectionMap.put(android.provider.Contacts.Groups.NAME,
454                android.provider.Contacts.Groups.NAME);
455        sGroupProjectionMap.put(android.provider.Contacts.Groups.NOTES,
456                android.provider.Contacts.Groups.NOTES);
457        sGroupProjectionMap.put(android.provider.Contacts.Groups.SYSTEM_ID,
458                android.provider.Contacts.Groups.SYSTEM_ID);
459
460        sGroupMembershipProjectionMap = new HashMap<String, String>(sGroupProjectionMap);
461        sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership._ID,
462                android.provider.Contacts.GroupMembership._ID);
463        sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.PERSON_ID,
464                android.provider.Contacts.GroupMembership.PERSON_ID);
465        sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.GROUP_ID,
466                android.provider.Contacts.GroupMembership.GROUP_ID);
467        sGroupMembershipProjectionMap.put(
468                android.provider.Contacts.GroupMembership.GROUP_SYNC_ID,
469                android.provider.Contacts.GroupMembership.GROUP_SYNC_ID);
470        sGroupMembershipProjectionMap.put(
471                android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT,
472                android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT);
473        sGroupMembershipProjectionMap.put(
474                android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE,
475                android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE);
476
477        sPhotoProjectionMap = new HashMap<String, String>();
478        sPhotoProjectionMap.put(android.provider.Contacts.Photos._ID,
479                android.provider.Contacts.Photos._ID);
480        sPhotoProjectionMap.put(android.provider.Contacts.Photos.PERSON_ID,
481                android.provider.Contacts.Photos.PERSON_ID);
482        sPhotoProjectionMap.put(android.provider.Contacts.Photos.DATA,
483                android.provider.Contacts.Photos.DATA);
484        sPhotoProjectionMap.put(android.provider.Contacts.Photos.LOCAL_VERSION,
485                android.provider.Contacts.Photos.LOCAL_VERSION);
486        sPhotoProjectionMap.put(android.provider.Contacts.Photos.DOWNLOAD_REQUIRED,
487                android.provider.Contacts.Photos.DOWNLOAD_REQUIRED);
488        sPhotoProjectionMap.put(android.provider.Contacts.Photos.EXISTS_ON_SERVER,
489                android.provider.Contacts.Photos.EXISTS_ON_SERVER);
490        sPhotoProjectionMap.put(android.provider.Contacts.Photos.SYNC_ERROR,
491                android.provider.Contacts.Photos.SYNC_ERROR);
492    }
493
494    private final Context mContext;
495    private final ContactsDatabaseHelper mDbHelper;
496    private final ContactsProvider2 mContactsProvider;
497    private final NameSplitter mPhoneticNameSplitter;
498    private final GlobalSearchSupport mGlobalSearchSupport;
499
500    private final SQLiteStatement mDataMimetypeQuery;
501    private final SQLiteStatement mDataRawContactIdQuery;
502
503    private final ContentValues mValues = new ContentValues();
504    private final ContentValues mValues2 = new ContentValues();
505    private final ContentValues mValues3 = new ContentValues();
506    private boolean mDefaultAccountKnown;
507    private Account mAccount;
508
509    private long mMimetypeEmail;
510    private long mMimetypeIm;
511    private long mMimetypePostal;
512
513
514    public LegacyApiSupport(Context context, ContactsDatabaseHelper contactsDatabaseHelper,
515            ContactsProvider2 contactsProvider, GlobalSearchSupport globalSearchSupport) {
516        mContext = context;
517        mContactsProvider = contactsProvider;
518        mDbHelper = contactsDatabaseHelper;
519        mGlobalSearchSupport = globalSearchSupport;
520
521        mPhoneticNameSplitter = new NameSplitter("", "", "", context
522                .getString(com.android.internal.R.string.common_name_conjunctions), Locale
523                .getDefault());
524
525        SQLiteDatabase db = mDbHelper.getReadableDatabase();
526        mDataMimetypeQuery = db.compileStatement(
527                "SELECT " + DataColumns.MIMETYPE_ID +
528                " FROM " + Tables.DATA +
529                " WHERE " + Data._ID + "=?");
530
531        mDataRawContactIdQuery = db.compileStatement(
532                "SELECT " + Data.RAW_CONTACT_ID +
533                " FROM " + Tables.DATA +
534                " WHERE " + Data._ID + "=?");
535
536        mMimetypeEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
537        mMimetypeIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
538        mMimetypePostal = mDbHelper.getMimeTypeId(StructuredPostal.CONTENT_ITEM_TYPE);
539    }
540
541    private void ensureDefaultAccount() {
542        if (!mDefaultAccountKnown) {
543            mAccount = mContactsProvider.getDefaultAccount();
544            mDefaultAccountKnown = true;
545        }
546    }
547
548    public static void createDatabase(SQLiteDatabase db) {
549        Log.i(TAG, "Bootstrapping database legacy support");
550        createViews(db);
551        createSettingsTable(db);
552    }
553
554    public static void createViews(SQLiteDatabase db) {
555
556        String peopleColumns = "name." + StructuredName.DISPLAY_NAME
557                        + " AS " + People.NAME + ", " +
558                Tables.RAW_CONTACTS + "." + RawContactsColumns.DISPLAY_NAME
559                        + " AS " + People.DISPLAY_NAME + ", " +
560                PHONETIC_NAME_SQL
561                        + " AS " + People.PHONETIC_NAME + " , " +
562                "note." + Note.NOTE
563                        + " AS " + People.NOTES + ", " +
564                RawContacts.ACCOUNT_NAME + ", " +
565                RawContacts.ACCOUNT_TYPE + ", " +
566                Tables.RAW_CONTACTS + "." + RawContacts.TIMES_CONTACTED
567                        + " AS " + People.TIMES_CONTACTED + ", " +
568                Tables.RAW_CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED
569                        + " AS " + People.LAST_TIME_CONTACTED + ", " +
570                Tables.RAW_CONTACTS + "." + RawContacts.CUSTOM_RINGTONE
571                        + " AS " + People.CUSTOM_RINGTONE + ", " +
572                Tables.RAW_CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL
573                        + " AS " + People.SEND_TO_VOICEMAIL + ", " +
574                Tables.RAW_CONTACTS + "." + RawContacts.STARRED
575                        + " AS " + People.STARRED + ", " +
576                "organization." + Data._ID
577                        + " AS " + People.PRIMARY_ORGANIZATION_ID + ", " +
578                "email." + Data._ID
579                        + " AS " + People.PRIMARY_EMAIL_ID + ", " +
580                "phone." + Data._ID
581                        + " AS " + People.PRIMARY_PHONE_ID + ", " +
582                "phone." + Phone.NUMBER
583                        + " AS " + People.NUMBER + ", " +
584                "phone." + Phone.TYPE
585                        + " AS " + People.TYPE + ", " +
586                "phone." + Phone.LABEL
587                        + " AS " + People.LABEL + ", " +
588                "phone." + PhoneColumns.NORMALIZED_NUMBER
589                        + " AS " + People.NUMBER_KEY;
590
591        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PEOPLE + ";");
592        db.execSQL("CREATE VIEW " + LegacyTables.PEOPLE + " AS SELECT " +
593                RawContactsColumns.CONCRETE_ID
594                        + " AS " + android.provider.Contacts.People._ID + ", " +
595                peopleColumns +
596                " FROM " + Tables.RAW_CONTACTS + PEOPLE_JOINS +
597                " WHERE " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
598                " AND " + RawContacts.IS_RESTRICTED + "=0" + ";");
599
600        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.ORGANIZATIONS + ";");
601        db.execSQL("CREATE VIEW " + LegacyTables.ORGANIZATIONS + " AS SELECT " +
602                DataColumns.CONCRETE_ID
603                        + " AS " + android.provider.Contacts.Organizations._ID + ", " +
604                Data.RAW_CONTACT_ID
605                        + " AS " + android.provider.Contacts.Organizations.PERSON_ID + ", " +
606                Data.IS_PRIMARY
607                        + " AS " + android.provider.Contacts.Organizations.ISPRIMARY + ", " +
608                RawContacts.ACCOUNT_NAME + ", " +
609                RawContacts.ACCOUNT_TYPE + ", " +
610                Organization.COMPANY
611                        + " AS " + android.provider.Contacts.Organizations.COMPANY + ", " +
612                Organization.TYPE
613                        + " AS " + android.provider.Contacts.Organizations.TYPE + ", " +
614                Organization.LABEL
615                        + " AS " + android.provider.Contacts.Organizations.LABEL + ", " +
616                Organization.TITLE
617                        + " AS " + android.provider.Contacts.Organizations.TITLE +
618                " FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
619                " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
620                        + Organization.CONTENT_ITEM_TYPE + "'"
621                        + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
622                        + " AND " + RawContacts.IS_RESTRICTED + "=0" +
623        ";");
624
625        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.CONTACT_METHODS + ";");
626        db.execSQL("CREATE VIEW " + LegacyTables.CONTACT_METHODS + " AS SELECT " +
627                DataColumns.CONCRETE_ID
628                        + " AS " + ContactMethods._ID + ", " +
629                DataColumns.CONCRETE_RAW_CONTACT_ID
630                        + " AS " + ContactMethods.PERSON_ID + ", " +
631                CONTACT_METHOD_KIND_SQL
632                        + " AS " + ContactMethods.KIND + ", " +
633                DataColumns.CONCRETE_IS_PRIMARY
634                        + " AS " + ContactMethods.ISPRIMARY + ", " +
635                Tables.DATA + "." + Email.TYPE
636                        + " AS " + ContactMethods.TYPE + ", " +
637                CONTACT_METHOD_DATA_SQL
638                        + " AS " + ContactMethods.DATA + ", " +
639                Tables.DATA + "." + Email.LABEL
640                        + " AS " + ContactMethods.LABEL + ", " +
641                DataColumns.CONCRETE_DATA14
642                        + " AS " + ContactMethods.AUX_DATA + ", " +
643                peopleColumns +
644                " FROM " + Tables.DATA + DATA_JOINS +
645                " WHERE " + ContactMethods.KIND + " IS NOT NULL"
646                    + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
647                    + " AND " + RawContacts.IS_RESTRICTED + "=0" +
648        ";");
649
650
651        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHONES + ";");
652        db.execSQL("CREATE VIEW " + LegacyTables.PHONES + " AS SELECT " +
653                DataColumns.CONCRETE_ID
654                        + " AS " + android.provider.Contacts.Phones._ID + ", " +
655                DataColumns.CONCRETE_RAW_CONTACT_ID
656                        + " AS " + android.provider.Contacts.Phones.PERSON_ID + ", " +
657                DataColumns.CONCRETE_IS_PRIMARY
658                        + " AS " + android.provider.Contacts.Phones.ISPRIMARY + ", " +
659                Tables.DATA + "." + Phone.NUMBER
660                        + " AS " + android.provider.Contacts.Phones.NUMBER + ", " +
661                Tables.DATA + "." + Phone.TYPE
662                        + " AS " + android.provider.Contacts.Phones.TYPE + ", " +
663                Tables.DATA + "." + Phone.LABEL
664                        + " AS " + android.provider.Contacts.Phones.LABEL + ", " +
665                Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.NORMALIZED_NUMBER
666                        + " AS " + android.provider.Contacts.Phones.NUMBER_KEY + ", " +
667                peopleColumns +
668                " FROM " + Tables.DATA
669                        + " JOIN " + Tables.PHONE_LOOKUP
670                        + " ON (" + Tables.DATA + "._id = "
671                                + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID + ")"
672                        + DATA_JOINS +
673                " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
674                        + Phone.CONTENT_ITEM_TYPE + "'"
675                        + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
676                        + " AND " + RawContacts.IS_RESTRICTED + "=0" +
677        ";");
678
679        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.EXTENSIONS + ";");
680        db.execSQL("CREATE VIEW " + LegacyTables.EXTENSIONS + " AS SELECT " +
681                DataColumns.CONCRETE_ID
682                        + " AS " + android.provider.Contacts.Extensions._ID + ", " +
683                DataColumns.CONCRETE_RAW_CONTACT_ID
684                        + " AS " + android.provider.Contacts.Extensions.PERSON_ID + ", " +
685                RawContacts.ACCOUNT_NAME + ", " +
686                RawContacts.ACCOUNT_TYPE + ", " +
687                ExtensionsColumns.NAME
688                        + " AS " + android.provider.Contacts.Extensions.NAME + ", " +
689                ExtensionsColumns.VALUE
690                        + " AS " + android.provider.Contacts.Extensions.VALUE +
691                " FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
692                " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
693                        + android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE + "'"
694                        + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
695                        + " AND " + RawContacts.IS_RESTRICTED + "=0" +
696        ";");
697
698        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUPS + ";");
699        db.execSQL("CREATE VIEW " + LegacyTables.GROUPS + " AS SELECT " +
700                GroupsColumns.CONCRETE_ID + " AS " + android.provider.Contacts.Groups._ID + ", " +
701                Groups.ACCOUNT_NAME + ", " +
702                Groups.ACCOUNT_TYPE + ", " +
703                Groups.TITLE + " AS " + android.provider.Contacts.Groups.NAME + ", " +
704                Groups.NOTES + " AS " + android.provider.Contacts.Groups.NOTES + " , " +
705                Groups.SYSTEM_ID + " AS " + android.provider.Contacts.Groups.SYSTEM_ID +
706                " FROM " + Tables.GROUPS +
707        ";");
708
709        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUP_MEMBERSHIP + ";");
710        db.execSQL("CREATE VIEW " + LegacyTables.GROUP_MEMBERSHIP + " AS SELECT " +
711                DataColumns.CONCRETE_ID
712                        + " AS " + android.provider.Contacts.GroupMembership._ID + ", " +
713                DataColumns.CONCRETE_RAW_CONTACT_ID
714                        + " AS " + android.provider.Contacts.GroupMembership.PERSON_ID + ", " +
715                Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_NAME
716                        + " AS " +  RawContacts.ACCOUNT_NAME + ", " +
717                Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_TYPE
718                        + " AS " +  RawContacts.ACCOUNT_TYPE + ", " +
719                GroupMembership.GROUP_ROW_ID
720                        + " AS " + android.provider.Contacts.GroupMembership.GROUP_ID + ", " +
721                Groups.TITLE
722                        + " AS " + android.provider.Contacts.GroupMembership.NAME + ", " +
723                Groups.NOTES
724                        + " AS " + android.provider.Contacts.GroupMembership.NOTES + ", " +
725                Groups.SYSTEM_ID
726                        + " AS " + android.provider.Contacts.GroupMembership.SYSTEM_ID + ", " +
727                GroupsColumns.CONCRETE_SOURCE_ID
728                        + " AS "
729                        + android.provider.Contacts.GroupMembership.GROUP_SYNC_ID + ", " +
730                GroupsColumns.CONCRETE_ACCOUNT_NAME
731                        + " AS "
732                        + android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT + ", " +
733                GroupsColumns.CONCRETE_ACCOUNT_TYPE
734                        + " AS "
735                        + android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE +
736                " FROM " + Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS +
737                " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
738                        + GroupMembership.CONTENT_ITEM_TYPE + "'"
739                        + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
740        ";");
741
742        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHOTOS + ";");
743        db.execSQL("CREATE VIEW " + LegacyTables.PHOTOS + " AS SELECT " +
744                DataColumns.CONCRETE_ID
745                        + " AS " + android.provider.Contacts.Photos._ID + ", " +
746                DataColumns.CONCRETE_RAW_CONTACT_ID
747                        + " AS " + android.provider.Contacts.Photos.PERSON_ID + ", " +
748                RawContacts.ACCOUNT_NAME + ", " +
749                RawContacts.ACCOUNT_TYPE + ", " +
750                Tables.DATA + "." + Photo.PHOTO
751                        + " AS " + android.provider.Contacts.Photos.DATA + ", " +
752                "legacy_photo." + LegacyPhotoData.EXISTS_ON_SERVER
753                        + " AS " + android.provider.Contacts.Photos.EXISTS_ON_SERVER + ", " +
754                "legacy_photo." + LegacyPhotoData.DOWNLOAD_REQUIRED
755                        + " AS " + android.provider.Contacts.Photos.DOWNLOAD_REQUIRED + ", " +
756                "legacy_photo." + LegacyPhotoData.LOCAL_VERSION
757                        + " AS " + android.provider.Contacts.Photos.LOCAL_VERSION + ", " +
758                "legacy_photo." + LegacyPhotoData.SYNC_ERROR
759                        + " AS " + android.provider.Contacts.Photos.SYNC_ERROR +
760                " FROM " + Tables.DATA + DATA_JOINS + LEGACY_PHOTO_JOIN +
761                " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
762                        + Photo.CONTENT_ITEM_TYPE + "'"
763                        + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
764                        + " AND " + RawContacts.IS_RESTRICTED + "=0" +
765        ";");
766
767    }
768
769    public static void createSettingsTable(SQLiteDatabase db) {
770        db.execSQL("DROP TABLE IF EXISTS " + LegacyTables.SETTINGS + ";");
771        db.execSQL("CREATE TABLE " + LegacyTables.SETTINGS + " (" +
772                android.provider.Contacts.Settings._ID + " INTEGER PRIMARY KEY," +
773                android.provider.Contacts.Settings._SYNC_ACCOUNT + " TEXT," +
774                android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE + " TEXT," +
775                android.provider.Contacts.Settings.KEY + " STRING NOT NULL," +
776                android.provider.Contacts.Settings.VALUE + " STRING " +
777        ");");
778    }
779
780    public Uri insert(Uri uri, ContentValues values) {
781        ensureDefaultAccount();
782        final int match = sUriMatcher.match(uri);
783        long id = 0;
784        switch (match) {
785            case PEOPLE:
786                id = insertPeople(values);
787                break;
788
789            case ORGANIZATIONS:
790                id = insertOrganization(values);
791                break;
792
793            case PEOPLE_CONTACTMETHODS: {
794                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
795                id = insertContactMethod(rawContactId, values);
796                break;
797            }
798
799            case CONTACTMETHODS: {
800                long rawContactId = getRequiredValue(values, ContactMethods.PERSON_ID);
801                id = insertContactMethod(rawContactId, values);
802                break;
803            }
804
805            case PHONES: {
806                long rawContactId = getRequiredValue(values,
807                        android.provider.Contacts.Phones.PERSON_ID);
808                id = insertPhone(rawContactId, values);
809                break;
810            }
811
812            case PEOPLE_PHONES: {
813                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
814                id = insertPhone(rawContactId, values);
815                break;
816            }
817
818            case EXTENSIONS: {
819                long rawContactId = getRequiredValue(values,
820                        android.provider.Contacts.Extensions.PERSON_ID);
821                id = insertExtension(rawContactId, values);
822                break;
823            }
824
825            case GROUPS:
826                id = insertGroup(values);
827                break;
828
829            case GROUPMEMBERSHIP: {
830                long rawContactId = getRequiredValue(values,
831                        android.provider.Contacts.GroupMembership.PERSON_ID);
832                long groupId = getRequiredValue(values,
833                        android.provider.Contacts.GroupMembership.GROUP_ID);
834                id = insertGroupMembership(rawContactId, groupId);
835                break;
836            }
837
838            default:
839                throw new UnsupportedOperationException("Unknown uri: " + uri);
840        }
841
842        if (id < 0) {
843            return null;
844        }
845
846        final Uri result = ContentUris.withAppendedId(uri, id);
847        onChange(result);
848        return result;
849    }
850
851    private long getRequiredValue(ContentValues values, String column) {
852        if (!values.containsKey(column)) {
853            throw new RuntimeException("Required value: " + column);
854        }
855
856        return values.getAsLong(column);
857    }
858
859    private long insertPeople(ContentValues values) {
860        parsePeopleValues(values);
861
862        Uri contactUri = mContactsProvider.insertInTransaction(RawContacts.CONTENT_URI, mValues);
863        long rawContactId = ContentUris.parseId(contactUri);
864
865        if (mValues2.size() != 0) {
866            mValues2.put(Data.RAW_CONTACT_ID, rawContactId);
867            mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues2);
868        }
869        if (mValues3.size() != 0) {
870            mValues3.put(Data.RAW_CONTACT_ID, rawContactId);
871            mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues3);
872        }
873
874        return rawContactId;
875    }
876
877    private long insertOrganization(ContentValues values) {
878        parseOrganizationValues(values);
879        ContactsDatabaseHelper.copyLongValue(mValues, Data.RAW_CONTACT_ID,
880                values, android.provider.Contacts.Organizations.PERSON_ID);
881
882        Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
883
884        return ContentUris.parseId(uri);
885    }
886
887    private long insertPhone(long rawContactId, ContentValues values) {
888        parsePhoneValues(values);
889        mValues.put(Data.RAW_CONTACT_ID, rawContactId);
890
891        Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
892
893        return ContentUris.parseId(uri);
894    }
895
896    private long insertContactMethod(long rawContactId, ContentValues values) {
897        Integer kind = values.getAsInteger(ContactMethods.KIND);
898        if (kind == null) {
899            throw new RuntimeException("Required value: " + ContactMethods.KIND);
900        }
901
902        parseContactMethodValues(kind, values);
903
904        mValues.put(Data.RAW_CONTACT_ID, rawContactId);
905        Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
906        return ContentUris.parseId(uri);
907    }
908
909    private long insertExtension(long rawContactId, ContentValues values) {
910        mValues.clear();
911
912        mValues.put(Data.RAW_CONTACT_ID, rawContactId);
913        mValues.put(Data.MIMETYPE, android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE);
914
915        parseExtensionValues(values);
916
917        Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
918        return ContentUris.parseId(uri);
919    }
920
921    private long insertGroup(ContentValues values) {
922        parseGroupValues(values);
923
924        if (mAccount != null) {
925            mValues.put(Groups.ACCOUNT_NAME, mAccount.name);
926            mValues.put(Groups.ACCOUNT_TYPE, mAccount.type);
927        }
928
929        Uri uri = mContactsProvider.insertInTransaction(Groups.CONTENT_URI, mValues);
930        return ContentUris.parseId(uri);
931    }
932
933    private long insertGroupMembership(long rawContactId, long groupId) {
934        mValues.clear();
935
936        mValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
937        mValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
938        mValues.put(GroupMembership.GROUP_ROW_ID, groupId);
939
940        Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
941        return ContentUris.parseId(uri);
942    }
943
944    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
945        ensureDefaultAccount();
946
947        int match = sUriMatcher.match(uri);
948        int count = 0;
949        switch(match) {
950            case PEOPLE_UPDATE_CONTACT_TIME: {
951                count = updateContactTime(uri, values);
952                break;
953            }
954
955            case PEOPLE_PHOTO: {
956                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
957                return updatePhoto(rawContactId, values);
958            }
959
960            case SETTINGS: {
961                return updateSettings(values);
962            }
963
964            case GROUPMEMBERSHIP:
965            case GROUPMEMBERSHIP_ID:
966            case -1: {
967                throw new UnsupportedOperationException("Unknown uri: " + uri);
968            }
969
970            default: {
971                count = updateAll(uri, match, values, selection, selectionArgs);
972            }
973        }
974
975        if (count > 0) {
976            mContext.getContentResolver().notifyChange(uri, null);
977        }
978
979        return count;
980    }
981
982    private int updateAll(Uri uri, final int match, ContentValues values, String selection,
983            String[] selectionArgs) {
984        Cursor c = query(uri, IdQuery.COLUMNS, selection, selectionArgs, null, null);
985        if (c == null) {
986            return 0;
987        }
988
989        int count = 0;
990        try {
991            while (c.moveToNext()) {
992                long id = c.getLong(IdQuery._ID);
993                count += update(match, id, values);
994            }
995        } finally {
996            c.close();
997        }
998
999        return count;
1000    }
1001
1002    public int update(int match, long id, ContentValues values) {
1003        int count = 0;
1004        switch(match) {
1005            case PEOPLE:
1006            case PEOPLE_ID: {
1007                count = updatePeople(id, values);
1008                break;
1009            }
1010
1011            case ORGANIZATIONS:
1012            case ORGANIZATIONS_ID: {
1013                count = updateOrganizations(id, values);
1014                break;
1015            }
1016
1017            case PHONES:
1018            case PHONES_ID: {
1019                count = updatePhones(id, values);
1020                break;
1021            }
1022
1023            case CONTACTMETHODS:
1024            case CONTACTMETHODS_ID: {
1025                count = updateContactMethods(id, values);
1026                break;
1027            }
1028
1029            case EXTENSIONS:
1030            case EXTENSIONS_ID: {
1031                count = updateExtensions(id, values);
1032                break;
1033            }
1034
1035            case GROUPS:
1036            case GROUPS_ID: {
1037                count = updateGroups(id, values);
1038                break;
1039            }
1040
1041            case PHOTOS:
1042            case PHOTOS_ID:
1043                count = updatePhotoByDataId(id, values);
1044                break;
1045        }
1046
1047        return count;
1048    }
1049
1050    private int updatePeople(long rawContactId, ContentValues values) {
1051        parsePeopleValues(values);
1052
1053        int count = mContactsProvider.update(RawContacts.CONTENT_URI,
1054                mValues, RawContacts._ID + "=" + rawContactId, null);
1055
1056        if (count == 0) {
1057            return 0;
1058        }
1059
1060        if (mValues2.size() != 0) {
1061            Uri dataUri = findFirstDataRow(rawContactId, StructuredName.CONTENT_ITEM_TYPE);
1062            if (dataUri != null) {
1063                mContactsProvider.update(dataUri, mValues2, null, null);
1064            } else {
1065                mValues2.put(Data.RAW_CONTACT_ID, rawContactId);
1066                mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues2);
1067            }
1068        }
1069
1070        if (mValues3.size() != 0) {
1071            Uri dataUri = findFirstDataRow(rawContactId, Note.CONTENT_ITEM_TYPE);
1072            if (dataUri != null) {
1073                mContactsProvider.update(dataUri, mValues3, null, null);
1074            } else {
1075                mValues3.put(Data.RAW_CONTACT_ID, rawContactId);
1076                mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues3);
1077            }
1078        }
1079
1080        return count;
1081    }
1082
1083    private int updateOrganizations(long dataId, ContentValues values) {
1084        parseOrganizationValues(values);
1085
1086        return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1087                Data._ID + "=" + dataId, null);
1088    }
1089
1090    private int updatePhones(long dataId, ContentValues values) {
1091        parsePhoneValues(values);
1092
1093        return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1094                Data._ID + "=" + dataId, null);
1095    }
1096
1097    private int updateContactMethods(long dataId, ContentValues values) {
1098        int kind;
1099
1100        mDataMimetypeQuery.bindLong(1, dataId);
1101        long mimetype_id;
1102        try {
1103            mimetype_id = mDataMimetypeQuery.simpleQueryForLong();
1104        } catch (SQLiteDoneException e) {
1105            // Data row not found
1106            return 0;
1107        }
1108
1109        if (mimetype_id == mMimetypeEmail) {
1110            kind = android.provider.Contacts.KIND_EMAIL;
1111        } else if (mimetype_id == mMimetypeIm) {
1112            kind = android.provider.Contacts.KIND_IM;
1113        } else if (mimetype_id == mMimetypePostal) {
1114            kind = android.provider.Contacts.KIND_POSTAL;
1115        } else {
1116
1117            // Non-legacy kind: return "Not found"
1118            return 0;
1119        }
1120
1121        parseContactMethodValues(kind, values);
1122
1123        return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1124                Data._ID + "=" + dataId, null);
1125    }
1126
1127    private int updateExtensions(long dataId, ContentValues values) {
1128        parseExtensionValues(values);
1129
1130        return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1131                Data._ID + "=" + dataId, null);
1132    }
1133
1134    private int updateGroups(long groupId, ContentValues values) {
1135        parseGroupValues(values);
1136
1137        return mContactsProvider.updateInTransaction(Groups.CONTENT_URI, mValues,
1138                Groups._ID + "=" + groupId, null);
1139    }
1140
1141    private int updateContactTime(Uri uri, ContentValues values) {
1142
1143        // TODO check sanctions
1144
1145        long lastTimeContacted;
1146        if (values.containsKey(People.LAST_TIME_CONTACTED)) {
1147            lastTimeContacted = values.getAsLong(People.LAST_TIME_CONTACTED);
1148        } else {
1149            lastTimeContacted = System.currentTimeMillis();
1150        }
1151
1152        long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
1153        long contactId = mDbHelper.getContactId(rawContactId);
1154        SQLiteDatabase mDb = mDbHelper.getWritableDatabase();
1155        mSelectionArgs2[0] = String.valueOf(lastTimeContacted);
1156        if (contactId != 0) {
1157            mSelectionArgs2[1] = String.valueOf(contactId);
1158            mDb.execSQL(CONTACTS_UPDATE_LASTTIMECONTACTED, mSelectionArgs2);
1159            // increment times_contacted column
1160            mSelectionArgs1[0] = String.valueOf(contactId);
1161            mDb.execSQL(ContactsProvider2.UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
1162        }
1163        mSelectionArgs2[1] = String.valueOf(rawContactId);
1164        mDb.execSQL(RAWCONTACTS_UPDATE_LASTTIMECONTACTED, mSelectionArgs2);
1165        // increment times_contacted column
1166        mSelectionArgs1[0] = String.valueOf(contactId);
1167        mDb.execSQL(ContactsProvider2.UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
1168        return 1;
1169    }
1170
1171    private int updatePhoto(long rawContactId, ContentValues values) {
1172
1173        // TODO check sanctions
1174
1175        int count;
1176
1177        long dataId = findFirstDataId(rawContactId, Photo.CONTENT_ITEM_TYPE);
1178
1179        mValues.clear();
1180        byte[] bytes = values.getAsByteArray(android.provider.Contacts.Photos.DATA);
1181        mValues.put(Photo.PHOTO, bytes);
1182
1183        if (dataId == -1) {
1184            mValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
1185            mValues.put(Data.RAW_CONTACT_ID, rawContactId);
1186            Uri dataUri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
1187            dataId = ContentUris.parseId(dataUri);
1188            count = 1;
1189        } else {
1190            Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
1191            count = mContactsProvider.updateInTransaction(dataUri, mValues, null, null);
1192        }
1193
1194        updateLegacyPhotoData(rawContactId, dataId, values);
1195
1196        return count;
1197    }
1198
1199    private int updatePhotoByDataId(long dataId, ContentValues values) {
1200
1201        mDataRawContactIdQuery.bindLong(1, dataId);
1202        long rawContactId;
1203
1204        try {
1205            rawContactId = mDataRawContactIdQuery.simpleQueryForLong();
1206        } catch (SQLiteDoneException e) {
1207            // Data row not found
1208            return 0;
1209        }
1210
1211        if (values.containsKey(android.provider.Contacts.Photos.DATA)) {
1212            byte[] bytes = values.getAsByteArray(android.provider.Contacts.Photos.DATA);
1213            mValues.clear();
1214            mValues.put(Photo.PHOTO, bytes);
1215            mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1216                    Data._ID + "=" + dataId, null);
1217        }
1218
1219        updateLegacyPhotoData(rawContactId, dataId, values);
1220
1221        return 1;
1222    }
1223
1224    private void updateLegacyPhotoData(long rawContactId, long dataId, ContentValues values) {
1225        mValues.clear();
1226        ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.LOCAL_VERSION,
1227                values, android.provider.Contacts.Photos.LOCAL_VERSION);
1228        ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.DOWNLOAD_REQUIRED,
1229                values, android.provider.Contacts.Photos.DOWNLOAD_REQUIRED);
1230        ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.EXISTS_ON_SERVER,
1231                values, android.provider.Contacts.Photos.EXISTS_ON_SERVER);
1232        ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.SYNC_ERROR,
1233                values, android.provider.Contacts.Photos.SYNC_ERROR);
1234
1235        int updated = mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1236                Data.MIMETYPE + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'"
1237                        + " AND " + Data.RAW_CONTACT_ID + "=" + rawContactId
1238                        + " AND " + LegacyPhotoData.PHOTO_DATA_ID + "=" + dataId, null);
1239        if (updated == 0) {
1240            mValues.put(Data.RAW_CONTACT_ID, rawContactId);
1241            mValues.put(Data.MIMETYPE, LegacyPhotoData.CONTENT_ITEM_TYPE);
1242            mValues.put(LegacyPhotoData.PHOTO_DATA_ID, dataId);
1243            mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
1244        }
1245    }
1246
1247    private int updateSettings(ContentValues values) {
1248        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1249        String accountName = values.getAsString(android.provider.Contacts.Settings._SYNC_ACCOUNT);
1250        String accountType =
1251                values.getAsString(android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE);
1252        String key = values.getAsString(android.provider.Contacts.Settings.KEY);
1253        if (key == null) {
1254            throw new IllegalArgumentException("you must specify the key when updating settings");
1255        }
1256        updateSetting(db, accountName, accountType, values);
1257        if (key.equals(android.provider.Contacts.Settings.SYNC_EVERYTHING)) {
1258            mValues.clear();
1259            mValues.put(Settings.SHOULD_SYNC,
1260                    values.getAsInteger(android.provider.Contacts.Settings.VALUE));
1261            String selection;
1262            String[] selectionArgs;
1263            if (accountName != null && accountType != null) {
1264                selectionArgs = new String[]{accountName, accountType};
1265                selection = Settings.ACCOUNT_NAME + "=?"
1266                        + " AND " + Settings.ACCOUNT_TYPE + "=?";
1267            } else {
1268                selectionArgs = null;
1269                selection = Settings.ACCOUNT_NAME + " IS NULL"
1270                        + " AND " + Settings.ACCOUNT_TYPE + " IS NULL";
1271            }
1272            int count = mContactsProvider.updateInTransaction(Settings.CONTENT_URI, mValues,
1273                    selection, selectionArgs);
1274            if (count == 0) {
1275                mValues.put(Settings.ACCOUNT_NAME, accountName);
1276                mValues.put(Settings.ACCOUNT_TYPE, accountType);
1277                mContactsProvider.insertInTransaction(Settings.CONTENT_URI, mValues);
1278            }
1279        }
1280        return 1;
1281    }
1282
1283    private void updateSetting(SQLiteDatabase db, String accountName, String accountType,
1284            ContentValues values) {
1285        final String key = values.getAsString(android.provider.Contacts.Settings.KEY);
1286        if (accountName == null || accountType == null) {
1287            db.delete(LegacyTables.SETTINGS, "_sync_account IS NULL AND key=?", new String[]{key});
1288        } else {
1289            db.delete(LegacyTables.SETTINGS, "_sync_account=? AND _sync_account_type=? AND key=?",
1290                    new String[]{accountName, accountType, key});
1291        }
1292        long rowId = db.insert(LegacyTables.SETTINGS,
1293                android.provider.Contacts.Settings.KEY, values);
1294        if (rowId < 0) {
1295            throw new SQLException("error updating settings with " + values);
1296        }
1297    }
1298
1299    private interface SettingsMatchQuery {
1300        String SQL =
1301            "SELECT "
1302                    + ContactsContract.Settings.ACCOUNT_NAME + ","
1303                    + ContactsContract.Settings.ACCOUNT_TYPE + ","
1304                    + ContactsContract.Settings.SHOULD_SYNC +
1305            " FROM " + Tables.SETTINGS + " LEFT OUTER JOIN " + LegacyTables.SETTINGS +
1306            " ON (" + ContactsContract.Settings.ACCOUNT_NAME + "="
1307                              + android.provider.Contacts.Settings._SYNC_ACCOUNT +
1308                      " AND " + ContactsContract.Settings.ACCOUNT_TYPE + "="
1309                              + android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE +
1310                      " AND " + android.provider.Contacts.Settings.KEY + "='"
1311                              + android.provider.Contacts.Settings.SYNC_EVERYTHING + "'" +
1312            ")" +
1313            " WHERE " + ContactsContract.Settings.SHOULD_SYNC + "<>"
1314                            + android.provider.Contacts.Settings.VALUE;
1315
1316        int ACCOUNT_NAME = 0;
1317        int ACCOUNT_TYPE = 1;
1318        int SHOULD_SYNC = 2;
1319    }
1320
1321    /**
1322     * Brings legacy settings table in sync with the new settings.
1323     */
1324    public void copySettingsToLegacySettings() {
1325        SQLiteDatabase db = mDbHelper.getWritableDatabase();
1326        Cursor cursor = db.rawQuery(SettingsMatchQuery.SQL, null);
1327        try {
1328            while(cursor.moveToNext()) {
1329                String accountName = cursor.getString(SettingsMatchQuery.ACCOUNT_NAME);
1330                String accountType = cursor.getString(SettingsMatchQuery.ACCOUNT_TYPE);
1331                String value = cursor.getString(SettingsMatchQuery.SHOULD_SYNC);
1332                mValues.clear();
1333                mValues.put(android.provider.Contacts.Settings._SYNC_ACCOUNT, accountName);
1334                mValues.put(android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE, accountType);
1335                mValues.put(android.provider.Contacts.Settings.KEY,
1336                        android.provider.Contacts.Settings.SYNC_EVERYTHING);
1337                mValues.put(android.provider.Contacts.Settings.VALUE, value);
1338                updateSetting(db, accountName, accountType, mValues);
1339            }
1340        } finally {
1341            cursor.close();
1342        }
1343    }
1344
1345    private void parsePeopleValues(ContentValues values) {
1346        mValues.clear();
1347        mValues2.clear();
1348        mValues3.clear();
1349
1350        ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
1351                values, People.CUSTOM_RINGTONE);
1352        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
1353                values, People.SEND_TO_VOICEMAIL);
1354        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
1355                values, People.LAST_TIME_CONTACTED);
1356        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
1357                values, People.TIMES_CONTACTED);
1358        ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
1359                values, People.STARRED);
1360        if (mAccount != null) {
1361            mValues.put(RawContacts.ACCOUNT_NAME, mAccount.name);
1362            mValues.put(RawContacts.ACCOUNT_TYPE, mAccount.type);
1363        }
1364
1365        if (values.containsKey(People.NAME) || values.containsKey(People.PHONETIC_NAME)) {
1366            mValues2.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
1367            ContactsDatabaseHelper.copyStringValue(mValues2, StructuredName.DISPLAY_NAME,
1368                    values, People.NAME);
1369            if (values.containsKey(People.PHONETIC_NAME)) {
1370                String phoneticName = values.getAsString(People.PHONETIC_NAME);
1371                NameSplitter.Name parsedName = new NameSplitter.Name();
1372                mPhoneticNameSplitter.split(parsedName, phoneticName);
1373                mValues2.put(StructuredName.PHONETIC_GIVEN_NAME, parsedName.getGivenNames());
1374                mValues2.put(StructuredName.PHONETIC_MIDDLE_NAME, parsedName.getMiddleName());
1375                mValues2.put(StructuredName.PHONETIC_FAMILY_NAME, parsedName.getFamilyName());
1376            }
1377        }
1378
1379        if (values.containsKey(People.NOTES)) {
1380            mValues3.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
1381            ContactsDatabaseHelper.copyStringValue(mValues3, Note.NOTE, values, People.NOTES);
1382        }
1383    }
1384
1385    private void parseOrganizationValues(ContentValues values) {
1386        mValues.clear();
1387
1388        mValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
1389
1390        ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY,
1391                values, android.provider.Contacts.Organizations.ISPRIMARY);
1392
1393        ContactsDatabaseHelper.copyStringValue(mValues, Organization.COMPANY,
1394                values, android.provider.Contacts.Organizations.COMPANY);
1395
1396        // TYPE values happen to remain the same between V1 and V2 - can just copy the value
1397        ContactsDatabaseHelper.copyLongValue(mValues, Organization.TYPE,
1398                values, android.provider.Contacts.Organizations.TYPE);
1399
1400        ContactsDatabaseHelper.copyStringValue(mValues, Organization.LABEL,
1401                values, android.provider.Contacts.Organizations.LABEL);
1402        ContactsDatabaseHelper.copyStringValue(mValues, Organization.TITLE,
1403                values, android.provider.Contacts.Organizations.TITLE);
1404    }
1405
1406    private void parsePhoneValues(ContentValues values) {
1407        mValues.clear();
1408
1409        mValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1410
1411        ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY,
1412                values, android.provider.Contacts.Phones.ISPRIMARY);
1413
1414        ContactsDatabaseHelper.copyStringValue(mValues, Phone.NUMBER,
1415                values, android.provider.Contacts.Phones.NUMBER);
1416
1417        // TYPE values happen to remain the same between V1 and V2 - can just copy the value
1418        ContactsDatabaseHelper.copyLongValue(mValues, Phone.TYPE,
1419                values, android.provider.Contacts.Phones.TYPE);
1420
1421        ContactsDatabaseHelper.copyStringValue(mValues, Phone.LABEL,
1422                values, android.provider.Contacts.Phones.LABEL);
1423    }
1424
1425    private void parseContactMethodValues(int kind, ContentValues values) {
1426        mValues.clear();
1427
1428        ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY, values,
1429                ContactMethods.ISPRIMARY);
1430
1431        switch (kind) {
1432            case android.provider.Contacts.KIND_EMAIL: {
1433                copyCommonFields(values, Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL,
1434                        Data.DATA14);
1435                ContactsDatabaseHelper.copyStringValue(mValues, Email.DATA, values,
1436                        ContactMethods.DATA);
1437                break;
1438            }
1439
1440            case android.provider.Contacts.KIND_IM: {
1441                String protocol = values.getAsString(ContactMethods.DATA);
1442                if (protocol.startsWith("pre:")) {
1443                    mValues.put(Im.PROTOCOL, Integer.parseInt(protocol.substring(4)));
1444                } else if (protocol.startsWith("custom:")) {
1445                    mValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
1446                    mValues.put(Im.CUSTOM_PROTOCOL, protocol.substring(7));
1447                }
1448
1449                copyCommonFields(values, Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL, Data.DATA14);
1450                break;
1451            }
1452
1453            case android.provider.Contacts.KIND_POSTAL: {
1454                copyCommonFields(values, StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE,
1455                        StructuredPostal.LABEL, Data.DATA14);
1456                ContactsDatabaseHelper.copyStringValue(mValues, StructuredPostal.FORMATTED_ADDRESS,
1457                        values, ContactMethods.DATA);
1458                break;
1459            }
1460        }
1461    }
1462
1463    private void copyCommonFields(ContentValues values, String mimeType, String typeColumn,
1464            String labelColumn, String auxDataColumn) {
1465        mValues.put(Data.MIMETYPE, mimeType);
1466        ContactsDatabaseHelper.copyLongValue(mValues, typeColumn, values,
1467                ContactMethods.TYPE);
1468        ContactsDatabaseHelper.copyStringValue(mValues, labelColumn, values,
1469                ContactMethods.LABEL);
1470        ContactsDatabaseHelper.copyStringValue(mValues, auxDataColumn, values,
1471                ContactMethods.AUX_DATA);
1472    }
1473
1474    private void parseGroupValues(ContentValues values) {
1475        mValues.clear();
1476
1477        ContactsDatabaseHelper.copyStringValue(mValues, Groups.TITLE,
1478                values, android.provider.Contacts.Groups.NAME);
1479        ContactsDatabaseHelper.copyStringValue(mValues, Groups.NOTES,
1480                values, android.provider.Contacts.Groups.NOTES);
1481        ContactsDatabaseHelper.copyStringValue(mValues, Groups.SYSTEM_ID,
1482                values, android.provider.Contacts.Groups.SYSTEM_ID);
1483    }
1484
1485    private void parseExtensionValues(ContentValues values) {
1486        ContactsDatabaseHelper.copyStringValue(mValues, ExtensionsColumns.NAME,
1487                values, android.provider.Contacts.People.Extensions.NAME);
1488        ContactsDatabaseHelper.copyStringValue(mValues, ExtensionsColumns.VALUE,
1489                values, android.provider.Contacts.People.Extensions.VALUE);
1490    }
1491
1492    private Uri findFirstDataRow(long rawContactId, String contentItemType) {
1493        long dataId = findFirstDataId(rawContactId, contentItemType);
1494        if (dataId == -1) {
1495            return null;
1496        }
1497
1498        return ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
1499    }
1500
1501    private long findFirstDataId(long rawContactId, String mimeType) {
1502        long dataId = -1;
1503        Cursor c = mContactsProvider.query(Data.CONTENT_URI, IdQuery.COLUMNS,
1504                Data.RAW_CONTACT_ID + "=" + rawContactId + " AND "
1505                        + Data.MIMETYPE + "='" + mimeType + "'",
1506                null, null);
1507        try {
1508            if (c.moveToFirst()) {
1509                dataId = c.getLong(IdQuery._ID);
1510            }
1511        } finally {
1512            c.close();
1513        }
1514        return dataId;
1515    }
1516
1517
1518    public int delete(Uri uri, String selection, String[] selectionArgs) {
1519        final int match = sUriMatcher.match(uri);
1520        if (match == -1 || match == SETTINGS) {
1521            throw new UnsupportedOperationException("Unknown uri: " + uri);
1522        }
1523
1524        Cursor c = query(uri, IdQuery.COLUMNS, selection, selectionArgs, null, null);
1525        if (c == null) {
1526            return 0;
1527        }
1528
1529        int count = 0;
1530        try {
1531            while (c.moveToNext()) {
1532                long id = c.getLong(IdQuery._ID);
1533                count += delete(uri, match, id);
1534            }
1535        } finally {
1536            c.close();
1537        }
1538
1539        return count;
1540    }
1541
1542    public int delete(Uri uri, int match, long id) {
1543        int count = 0;
1544        switch (match) {
1545            case PEOPLE:
1546            case PEOPLE_ID:
1547                count = mContactsProvider.deleteRawContact(id, mDbHelper.getContactId(id), false);
1548                break;
1549
1550            case PEOPLE_PHOTO:
1551                mValues.clear();
1552                mValues.putNull(android.provider.Contacts.Photos.DATA);
1553                updatePhoto(id, mValues);
1554                break;
1555
1556            case ORGANIZATIONS:
1557            case ORGANIZATIONS_ID:
1558                count = mContactsProvider.deleteData(id, ORGANIZATION_MIME_TYPES);
1559                break;
1560
1561            case CONTACTMETHODS:
1562            case CONTACTMETHODS_ID:
1563                count = mContactsProvider.deleteData(id, CONTACT_METHOD_MIME_TYPES);
1564                break;
1565
1566            case PHONES:
1567            case PHONES_ID:
1568                count = mContactsProvider.deleteData(id, PHONE_MIME_TYPES);
1569                break;
1570
1571            case EXTENSIONS:
1572            case EXTENSIONS_ID:
1573                count = mContactsProvider.deleteData(id, EXTENSION_MIME_TYPES);
1574                break;
1575
1576            case PHOTOS:
1577            case PHOTOS_ID:
1578                count = mContactsProvider.deleteData(id, PHOTO_MIME_TYPES);
1579                break;
1580
1581            case GROUPMEMBERSHIP:
1582            case GROUPMEMBERSHIP_ID:
1583                count = mContactsProvider.deleteData(id, GROUP_MEMBERSHIP_MIME_TYPES);
1584                break;
1585
1586            case GROUPS:
1587            case GROUPS_ID:
1588                count = mContactsProvider.deleteGroup(uri, id, false);
1589                break;
1590
1591            default:
1592                throw new UnsupportedOperationException("Unknown uri: " + uri);
1593        }
1594
1595        return count;
1596    }
1597
1598    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
1599            String sortOrder, String limit) {
1600        ensureDefaultAccount();
1601
1602        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
1603        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1604        String groupBy = null;
1605
1606        final int match = sUriMatcher.match(uri);
1607        switch (match) {
1608            case PEOPLE: {
1609                qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1610                qb.setProjectionMap(sPeopleProjectionMap);
1611                applyRawContactsAccount(qb);
1612                break;
1613            }
1614
1615            case PEOPLE_ID:
1616                qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1617                qb.setProjectionMap(sPeopleProjectionMap);
1618                applyRawContactsAccount(qb);
1619                qb.appendWhere(" AND " + People._ID + "=");
1620                qb.appendWhere(uri.getPathSegments().get(1));
1621                break;
1622
1623            case PEOPLE_FILTER: {
1624                qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1625                qb.setProjectionMap(sPeopleProjectionMap);
1626                applyRawContactsAccount(qb);
1627                String filterParam = uri.getPathSegments().get(2);
1628                qb.appendWhere(" AND " + People._ID + " IN "
1629                        + mContactsProvider.getRawContactsByFilterAsNestedQuery(filterParam));
1630                break;
1631            }
1632
1633            case GROUP_NAME_MEMBERS:
1634                qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1635                qb.setProjectionMap(sPeopleProjectionMap);
1636                applyRawContactsAccount(qb);
1637                String group = uri.getPathSegments().get(2);
1638                qb.appendWhere(" AND " + buildGroupNameMatchWhereClause(group));
1639                break;
1640
1641            case GROUP_SYSTEM_ID_MEMBERS:
1642                qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1643                qb.setProjectionMap(sPeopleProjectionMap);
1644                applyRawContactsAccount(qb);
1645                String systemId = uri.getPathSegments().get(2);
1646                qb.appendWhere(" AND " + buildGroupSystemIdMatchWhereClause(systemId));
1647                break;
1648
1649            case ORGANIZATIONS:
1650                qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
1651                qb.setProjectionMap(sOrganizationProjectionMap);
1652                applyRawContactsAccount(qb);
1653                break;
1654
1655            case ORGANIZATIONS_ID:
1656                qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
1657                qb.setProjectionMap(sOrganizationProjectionMap);
1658                applyRawContactsAccount(qb);
1659                qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "=");
1660                qb.appendWhere(uri.getPathSegments().get(1));
1661                break;
1662
1663            case PEOPLE_ORGANIZATIONS:
1664                qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
1665                qb.setProjectionMap(sOrganizationProjectionMap);
1666                applyRawContactsAccount(qb);
1667                qb.appendWhere(" AND " + android.provider.Contacts.Organizations.PERSON_ID + "=");
1668                qb.appendWhere(uri.getPathSegments().get(1));
1669                break;
1670
1671            case PEOPLE_ORGANIZATIONS_ID:
1672                qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
1673                qb.setProjectionMap(sOrganizationProjectionMap);
1674                applyRawContactsAccount(qb);
1675                qb.appendWhere(" AND " + android.provider.Contacts.Organizations.PERSON_ID + "=");
1676                qb.appendWhere(uri.getPathSegments().get(1));
1677                qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "=");
1678                qb.appendWhere(uri.getPathSegments().get(3));
1679                break;
1680
1681            case CONTACTMETHODS:
1682                qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
1683                qb.setProjectionMap(sContactMethodProjectionMap);
1684                applyRawContactsAccount(qb);
1685                break;
1686
1687            case CONTACTMETHODS_ID:
1688                qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
1689                qb.setProjectionMap(sContactMethodProjectionMap);
1690                applyRawContactsAccount(qb);
1691                qb.appendWhere(" AND " + ContactMethods._ID + "=");
1692                qb.appendWhere(uri.getPathSegments().get(1));
1693                break;
1694
1695            case CONTACTMETHODS_EMAIL:
1696                qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
1697                qb.setProjectionMap(sContactMethodProjectionMap);
1698                applyRawContactsAccount(qb);
1699                qb.appendWhere(" AND " + ContactMethods.KIND + "="
1700                        + android.provider.Contacts.KIND_EMAIL);
1701                break;
1702
1703            case PEOPLE_CONTACTMETHODS:
1704                qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
1705                qb.setProjectionMap(sContactMethodProjectionMap);
1706                applyRawContactsAccount(qb);
1707                qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "=");
1708                qb.appendWhere(uri.getPathSegments().get(1));
1709                qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL");
1710                break;
1711
1712            case PEOPLE_CONTACTMETHODS_ID:
1713                qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
1714                qb.setProjectionMap(sContactMethodProjectionMap);
1715                applyRawContactsAccount(qb);
1716                qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "=");
1717                qb.appendWhere(uri.getPathSegments().get(1));
1718                qb.appendWhere(" AND " + ContactMethods._ID + "=");
1719                qb.appendWhere(uri.getPathSegments().get(3));
1720                qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL");
1721                break;
1722
1723            case PHONES:
1724                qb.setTables(LegacyTables.PHONES + " phones");
1725                qb.setProjectionMap(sPhoneProjectionMap);
1726                applyRawContactsAccount(qb);
1727                break;
1728
1729            case PHONES_ID:
1730                qb.setTables(LegacyTables.PHONES + " phones");
1731                qb.setProjectionMap(sPhoneProjectionMap);
1732                applyRawContactsAccount(qb);
1733                qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "=");
1734                qb.appendWhere(uri.getPathSegments().get(1));
1735                break;
1736
1737            case PHONES_FILTER:
1738                qb.setTables(LegacyTables.PHONES + " phones");
1739                qb.setProjectionMap(sPhoneProjectionMap);
1740                applyRawContactsAccount(qb);
1741                if (uri.getPathSegments().size() > 2) {
1742                    String filterParam = uri.getLastPathSegment();
1743                    qb.appendWhere(" AND person =");
1744                    qb.appendWhere(mDbHelper.buildPhoneLookupAsNestedQuery(filterParam));
1745                }
1746                break;
1747
1748            case PEOPLE_PHONES:
1749                qb.setTables(LegacyTables.PHONES + " phones");
1750                qb.setProjectionMap(sPhoneProjectionMap);
1751                applyRawContactsAccount(qb);
1752                qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "=");
1753                qb.appendWhere(uri.getPathSegments().get(1));
1754                break;
1755
1756            case PEOPLE_PHONES_ID:
1757                qb.setTables(LegacyTables.PHONES + " phones");
1758                qb.setProjectionMap(sPhoneProjectionMap);
1759                applyRawContactsAccount(qb);
1760                qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "=");
1761                qb.appendWhere(uri.getPathSegments().get(1));
1762                qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "=");
1763                qb.appendWhere(uri.getPathSegments().get(3));
1764                break;
1765
1766            case EXTENSIONS:
1767                qb.setTables(LegacyTables.EXTENSIONS + " extensions");
1768                qb.setProjectionMap(sExtensionProjectionMap);
1769                applyRawContactsAccount(qb);
1770                break;
1771
1772            case EXTENSIONS_ID:
1773                qb.setTables(LegacyTables.EXTENSIONS + " extensions");
1774                qb.setProjectionMap(sExtensionProjectionMap);
1775                applyRawContactsAccount(qb);
1776                qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "=");
1777                qb.appendWhere(uri.getPathSegments().get(1));
1778                break;
1779
1780            case PEOPLE_EXTENSIONS:
1781                qb.setTables(LegacyTables.EXTENSIONS + " extensions");
1782                qb.setProjectionMap(sExtensionProjectionMap);
1783                applyRawContactsAccount(qb);
1784                qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "=");
1785                qb.appendWhere(uri.getPathSegments().get(1));
1786                break;
1787
1788            case PEOPLE_EXTENSIONS_ID:
1789                qb.setTables(LegacyTables.EXTENSIONS + " extensions");
1790                qb.setProjectionMap(sExtensionProjectionMap);
1791                applyRawContactsAccount(qb);
1792                qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "=");
1793                qb.appendWhere(uri.getPathSegments().get(1));
1794                qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "=");
1795                qb.appendWhere(uri.getPathSegments().get(3));
1796                break;
1797
1798            case GROUPS:
1799                qb.setTables(LegacyTables.GROUPS + " groups");
1800                qb.setProjectionMap(sGroupProjectionMap);
1801                applyGroupAccount(qb);
1802                break;
1803
1804            case GROUPS_ID:
1805                qb.setTables(LegacyTables.GROUPS + " groups");
1806                qb.setProjectionMap(sGroupProjectionMap);
1807                applyGroupAccount(qb);
1808                qb.appendWhere(" AND " + android.provider.Contacts.Groups._ID + "=");
1809                qb.appendWhere(uri.getPathSegments().get(1));
1810                break;
1811
1812            case GROUPMEMBERSHIP:
1813                qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
1814                qb.setProjectionMap(sGroupMembershipProjectionMap);
1815                applyRawContactsAccount(qb);
1816                break;
1817
1818            case GROUPMEMBERSHIP_ID:
1819                qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
1820                qb.setProjectionMap(sGroupMembershipProjectionMap);
1821                applyRawContactsAccount(qb);
1822                qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "=");
1823                qb.appendWhere(uri.getPathSegments().get(1));
1824                break;
1825
1826            case PEOPLE_GROUPMEMBERSHIP:
1827                qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
1828                qb.setProjectionMap(sGroupMembershipProjectionMap);
1829                applyRawContactsAccount(qb);
1830                qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "=");
1831                qb.appendWhere(uri.getPathSegments().get(1));
1832                break;
1833
1834            case PEOPLE_GROUPMEMBERSHIP_ID:
1835                qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
1836                qb.setProjectionMap(sGroupMembershipProjectionMap);
1837                applyRawContactsAccount(qb);
1838                qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "=");
1839                qb.appendWhere(uri.getPathSegments().get(1));
1840                qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "=");
1841                qb.appendWhere(uri.getPathSegments().get(3));
1842                break;
1843
1844            case PEOPLE_PHOTO:
1845                qb.setTables(LegacyTables.PHOTOS + " photos");
1846                qb.setProjectionMap(sPhotoProjectionMap);
1847                applyRawContactsAccount(qb);
1848                qb.appendWhere(" AND " + android.provider.Contacts.Photos.PERSON_ID + "=");
1849                qb.appendWhere(uri.getPathSegments().get(1));
1850                limit = "1";
1851                break;
1852
1853            case PHOTOS:
1854                qb.setTables(LegacyTables.PHOTOS + " photos");
1855                qb.setProjectionMap(sPhotoProjectionMap);
1856                applyRawContactsAccount(qb);
1857                break;
1858
1859            case PHOTOS_ID:
1860                qb.setTables(LegacyTables.PHOTOS + " photos");
1861                qb.setProjectionMap(sPhotoProjectionMap);
1862                applyRawContactsAccount(qb);
1863                qb.appendWhere(" AND " + android.provider.Contacts.Photos._ID + "=");
1864                qb.appendWhere(uri.getPathSegments().get(1));
1865                break;
1866
1867            case SEARCH_SUGGESTIONS:
1868
1869                // No legacy compatibility for search suggestions
1870                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
1871
1872            case SEARCH_SHORTCUT: {
1873                String lookupKey = uri.getLastPathSegment();
1874                return mGlobalSearchSupport.handleSearchShortcutRefresh(db, lookupKey, projection);
1875            }
1876
1877            case LIVE_FOLDERS_PEOPLE:
1878                return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_URI,
1879                        projection, selection, selectionArgs, sortOrder);
1880
1881            case LIVE_FOLDERS_PEOPLE_WITH_PHONES:
1882                return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_WITH_PHONES_URI,
1883                        projection, selection, selectionArgs, sortOrder);
1884
1885            case LIVE_FOLDERS_PEOPLE_FAVORITES:
1886                return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_FAVORITES_URI,
1887                        projection, selection, selectionArgs, sortOrder);
1888
1889            case LIVE_FOLDERS_PEOPLE_GROUP_NAME:
1890                return mContactsProvider.query(Uri.withAppendedPath(LIVE_FOLDERS_CONTACTS_URI,
1891                        Uri.encode(uri.getLastPathSegment())),
1892                        projection, selection, selectionArgs, sortOrder);
1893
1894            case DELETED_PEOPLE:
1895            case DELETED_GROUPS:
1896                throw new UnsupportedOperationException();
1897
1898            case SETTINGS:
1899                copySettingsToLegacySettings();
1900                qb.setTables(LegacyTables.SETTINGS);
1901                break;
1902
1903            default:
1904                throw new IllegalArgumentException("Unknown URL " + uri);
1905        }
1906
1907        // Perform the query and set the notification uri
1908        final Cursor c = qb.query(db, projection, selection, selectionArgs,
1909                groupBy, null, sortOrder, limit);
1910        if (c != null) {
1911            c.setNotificationUri(mContext.getContentResolver(),
1912                    android.provider.Contacts.CONTENT_URI);
1913        }
1914        return c;
1915    }
1916
1917    private void applyRawContactsAccount(SQLiteQueryBuilder qb) {
1918        StringBuilder sb = new StringBuilder();
1919        appendRawContactsAccount(sb);
1920        qb.appendWhere(sb.toString());
1921    }
1922
1923    private void appendRawContactsAccount(StringBuilder sb) {
1924        if (mAccount != null) {
1925            sb.append(RawContacts.ACCOUNT_NAME + "=");
1926            DatabaseUtils.appendEscapedSQLString(sb, mAccount.name);
1927            sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
1928            DatabaseUtils.appendEscapedSQLString(sb, mAccount.type);
1929        } else {
1930            sb.append(RawContacts.ACCOUNT_NAME + " IS NULL" +
1931                    " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL");
1932        }
1933    }
1934
1935    private void applyGroupAccount(SQLiteQueryBuilder qb) {
1936        StringBuilder sb = new StringBuilder();
1937        appendGroupAccount(sb);
1938        qb.appendWhere(sb.toString());
1939    }
1940
1941    private void appendGroupAccount(StringBuilder sb) {
1942        if (mAccount != null) {
1943            sb.append(Groups.ACCOUNT_NAME + "=");
1944            DatabaseUtils.appendEscapedSQLString(sb, mAccount.name);
1945            sb.append(" AND " + Groups.ACCOUNT_TYPE + "=");
1946            DatabaseUtils.appendEscapedSQLString(sb, mAccount.type);
1947        } else {
1948            sb.append(Groups.ACCOUNT_NAME + " IS NULL" +
1949                    " AND " + Groups.ACCOUNT_TYPE + " IS NULL");
1950        }
1951    }
1952
1953    /**
1954     * Build a WHERE clause that restricts the query to match people that are a member of
1955     * a group with a particular name. The projection map of the query must include
1956     * {@link People#_ID}.
1957     *
1958     * @param groupName The name of the group
1959     * @return The where clause.
1960     */
1961    private String buildGroupNameMatchWhereClause(String groupName) {
1962        return "people._id IN "
1963                + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
1964                + " FROM " + Tables.DATA_JOIN_MIMETYPES
1965                + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
1966                        + "' AND " + GroupMembership.GROUP_ROW_ID + "="
1967                                + "(SELECT " + Tables.GROUPS + "." + Groups._ID
1968                                + " FROM " + Tables.GROUPS
1969                                + " WHERE " + Groups.TITLE + "="
1970                                        + DatabaseUtils.sqlEscapeString(groupName) + "))";
1971    }
1972
1973    /**
1974     * Build a WHERE clause that restricts the query to match people that are a member of
1975     * a group with a particular system id. The projection map of the query must include
1976     * {@link People#_ID}.
1977     *
1978     * @param groupName The name of the group
1979     * @return The where clause.
1980     */
1981    private String buildGroupSystemIdMatchWhereClause(String systemId) {
1982        return "people._id IN "
1983                + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
1984                + " FROM " + Tables.DATA_JOIN_MIMETYPES
1985                + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
1986                        + "' AND " + GroupMembership.GROUP_ROW_ID + "="
1987                                + "(SELECT " + Tables.GROUPS + "." + Groups._ID
1988                                + " FROM " + Tables.GROUPS
1989                                + " WHERE " + Groups.SYSTEM_ID + "="
1990                                        + DatabaseUtils.sqlEscapeString(systemId) + "))";
1991    }
1992
1993    /**
1994     * Called when a change has been made.
1995     *
1996     * @param uri the uri that the change was made to
1997     */
1998    private void onChange(Uri uri) {
1999        mContext.getContentResolver().notifyChange(android.provider.Contacts.CONTENT_URI, null);
2000    }
2001
2002    public String getType(Uri uri) {
2003        int match = sUriMatcher.match(uri);
2004        switch (match) {
2005            case EXTENSIONS:
2006            case PEOPLE_EXTENSIONS:
2007                return Extensions.CONTENT_TYPE;
2008            case EXTENSIONS_ID:
2009            case PEOPLE_EXTENSIONS_ID:
2010                return Extensions.CONTENT_ITEM_TYPE;
2011            case PEOPLE:
2012                return "vnd.android.cursor.dir/person";
2013            case PEOPLE_ID:
2014                return "vnd.android.cursor.item/person";
2015            case PEOPLE_PHONES:
2016                return "vnd.android.cursor.dir/phone";
2017            case PEOPLE_PHONES_ID:
2018                return "vnd.android.cursor.item/phone";
2019            case PEOPLE_CONTACTMETHODS:
2020                return "vnd.android.cursor.dir/contact-methods";
2021            case PEOPLE_CONTACTMETHODS_ID:
2022                return getContactMethodType(uri);
2023            case PHONES:
2024                return "vnd.android.cursor.dir/phone";
2025            case PHONES_ID:
2026                return "vnd.android.cursor.item/phone";
2027            case PHONES_FILTER:
2028                return "vnd.android.cursor.dir/phone";
2029            case PHOTOS_ID:
2030                return "vnd.android.cursor.item/photo";
2031            case PHOTOS:
2032                return "vnd.android.cursor.dir/photo";
2033            case PEOPLE_PHOTO:
2034                return "vnd.android.cursor.item/photo";
2035            case CONTACTMETHODS:
2036                return "vnd.android.cursor.dir/contact-methods";
2037            case CONTACTMETHODS_ID:
2038                return getContactMethodType(uri);
2039            case ORGANIZATIONS:
2040                return "vnd.android.cursor.dir/organizations";
2041            case ORGANIZATIONS_ID:
2042                return "vnd.android.cursor.item/organization";
2043            case SEARCH_SUGGESTIONS:
2044                return SearchManager.SUGGEST_MIME_TYPE;
2045            case SEARCH_SHORTCUT:
2046                return SearchManager.SHORTCUT_MIME_TYPE;
2047            default:
2048                throw new IllegalArgumentException("Unknown URI");
2049        }
2050    }
2051
2052    private String getContactMethodType(Uri url) {
2053        String mime = null;
2054
2055        Cursor c = query(url, new String[] {ContactMethods.KIND}, null, null, null, null);
2056        if (c != null) {
2057            try {
2058                if (c.moveToFirst()) {
2059                    int kind = c.getInt(0);
2060                    switch (kind) {
2061                    case android.provider.Contacts.KIND_EMAIL:
2062                        mime = "vnd.android.cursor.item/email";
2063                        break;
2064
2065                    case android.provider.Contacts.KIND_IM:
2066                        mime = "vnd.android.cursor.item/jabber-im";
2067                        break;
2068
2069                    case android.provider.Contacts.KIND_POSTAL:
2070                        mime = "vnd.android.cursor.item/postal-address";
2071                        break;
2072                    }
2073                }
2074            } finally {
2075                c.close();
2076            }
2077        }
2078        return mime;
2079    }
2080}
2081