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