LegacyApiSupport.java revision caa1cf4ef062f163ac5e370cebc0e47b5ae7460e
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.OpenHelper.DataColumns;
19import com.android.providers.contacts.OpenHelper.ExtensionsColumns;
20import com.android.providers.contacts.OpenHelper.GroupsColumns;
21import com.android.providers.contacts.OpenHelper.MimetypesColumns;
22import com.android.providers.contacts.OpenHelper.PhoneColumns;
23import com.android.providers.contacts.OpenHelper.RawContactsColumns;
24import com.android.providers.contacts.OpenHelper.Tables;
25
26import android.accounts.Account;
27import android.accounts.AccountManager;
28import android.app.SearchManager;
29import android.content.ContentUris;
30import android.content.ContentValues;
31import android.content.Context;
32import android.content.UriMatcher;
33import android.database.Cursor;
34import android.database.DatabaseUtils;
35import android.database.sqlite.SQLiteDatabase;
36import android.database.sqlite.SQLiteQueryBuilder;
37import android.database.sqlite.SQLiteStatement;
38import android.net.Uri;
39import android.provider.ContactsContract;
40import android.provider.Contacts.ContactMethods;
41import android.provider.Contacts.People;
42import android.provider.ContactsContract.Data;
43import android.provider.ContactsContract.Groups;
44import android.provider.ContactsContract.Presence;
45import android.provider.ContactsContract.RawContacts;
46import android.provider.ContactsContract.CommonDataKinds.Email;
47import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
48import android.provider.ContactsContract.CommonDataKinds.Im;
49import android.provider.ContactsContract.CommonDataKinds.Note;
50import android.provider.ContactsContract.CommonDataKinds.Organization;
51import android.provider.ContactsContract.CommonDataKinds.Phone;
52import android.provider.ContactsContract.CommonDataKinds.Photo;
53import android.provider.ContactsContract.CommonDataKinds.StructuredName;
54import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
55import android.util.Log;
56
57import java.util.HashMap;
58
59public class LegacyApiSupport implements OpenHelper.Delegate {
60
61    private static final String TAG = "ContactsProviderV1";
62
63    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
64
65    private static final int PEOPLE = 1;
66    private static final int PEOPLE_ID = 2;
67    private static final int PEOPLE_UPDATE_CONTACT_TIME = 3;
68    private static final int ORGANIZATIONS = 4;
69    private static final int ORGANIZATIONS_ID = 5;
70    private static final int PEOPLE_CONTACTMETHODS = 6;
71    private static final int PEOPLE_CONTACTMETHODS_ID = 7;
72    private static final int CONTACTMETHODS = 8;
73    private static final int CONTACTMETHODS_ID = 9;
74    private static final int PEOPLE_PHONES = 10;
75    private static final int PEOPLE_PHONES_ID = 11;
76    private static final int PHONES = 12;
77    private static final int PHONES_ID = 13;
78    private static final int EXTENSIONS = 14;
79    private static final int EXTENSIONS_ID = 15;
80    private static final int PEOPLE_EXTENSIONS = 16;
81    private static final int PEOPLE_EXTENSIONS_ID = 17;
82    private static final int GROUPS = 18;
83    private static final int GROUPS_ID = 19;
84    private static final int GROUPMEMBERSHIP = 20;
85    private static final int GROUPMEMBERSHIP_ID = 21;
86    private static final int PEOPLE_GROUPMEMBERSHIP = 22;
87    private static final int PEOPLE_GROUPMEMBERSHIP_ID = 23;
88    private static final int PEOPLE_PHOTO = 24;
89    private static final int PHOTOS = 25;
90    private static final int PHOTOS_ID = 26;
91    private static final int PRESENCE = 27;
92    private static final int PRESENCE_ID = 28;
93    private static final int PEOPLE_FILTER = 29;
94    private static final int DELETED_PEOPLE = 30;
95    private static final int DELETED_GROUPS = 31;
96    private static final int SEARCH_SUGGESTIONS = 32;
97
98    private static final String PEOPLE_JOINS =
99            " LEFT OUTER JOIN data name ON (raw_contacts._id = name.raw_contact_id"
100            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = name.mimetype_id)"
101                    + "='" + StructuredName.CONTENT_ITEM_TYPE + "')"
102            + " LEFT OUTER JOIN data organization ON (raw_contacts._id = organization.raw_contact_id"
103            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = organization.mimetype_id)"
104                    + "='" + Organization.CONTENT_ITEM_TYPE + "' AND organization.is_primary)"
105            + " LEFT OUTER JOIN data email ON (raw_contacts._id = email.raw_contact_id"
106            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = email.mimetype_id)"
107                    + "='" + Email.CONTENT_ITEM_TYPE + "' AND email.is_primary)"
108            + " LEFT OUTER JOIN data note ON (raw_contacts._id = note.raw_contact_id"
109            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = note.mimetype_id)"
110                    + "='" + Note.CONTENT_ITEM_TYPE + "')"
111            + " LEFT OUTER JOIN data phone ON (raw_contacts._id = phone.raw_contact_id"
112            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = phone.mimetype_id)"
113                    + "='" + Phone.CONTENT_ITEM_TYPE + "' AND phone.is_primary)";
114
115    public static final String DATA_JOINS =
116            " JOIN mimetypes ON (mimetypes._id = data.mimetype_id)"
117            + " JOIN raw_contacts ON (raw_contacts._id = data.raw_contact_id)"
118            + PEOPLE_JOINS;
119
120    public static final String PRESENCE_JOINS =
121            " LEFT OUTER JOIN presence ON ("
122            + " presence.presence_id = (SELECT max(presence_id) FROM presence"
123            + " WHERE view_v1_people._id = presence_raw_contact_id))";
124
125    private static final String PHONETIC_NAME_SQL = "trim(trim("
126            + "ifnull(name." + StructuredName.PHONETIC_GIVEN_NAME + ",' ')||' '||"
127            + "ifnull(name." + StructuredName.PHONETIC_MIDDLE_NAME + ",' '))||' '||"
128            + "ifnull(name." + StructuredName.PHONETIC_FAMILY_NAME + ",' ')) ";
129
130    private static final String CONTACT_METHOD_KIND_SQL =
131            "CAST ((CASE WHEN mimetype='" + Email.CONTENT_ITEM_TYPE + "'"
132                + " THEN " + android.provider.Contacts.KIND_EMAIL
133                + " ELSE"
134                    + " (CASE WHEN mimetype='" + Im.CONTENT_ITEM_TYPE +"'"
135                        + " THEN " + android.provider.Contacts.KIND_IM
136                        + " ELSE"
137                        + " (CASE WHEN mimetype='" + StructuredPostal.CONTENT_ITEM_TYPE + "'"
138                            + " THEN "  + android.provider.Contacts.KIND_POSTAL
139                            + " ELSE"
140                                + " NULL"
141                            + " END)"
142                        + " END)"
143                + " END) AS INTEGER)";
144
145    public interface LegacyTables {
146        public static final String PEOPLE = "view_v1_people";
147        public static final String PEOPLE_JOIN_PRESENCE = "view_v1_people" + PRESENCE_JOINS;
148        public static final String GROUPS = "view_v1_groups";
149        public static final String ORGANIZATIONS = "view_v1_organizations";
150        public static final String CONTACT_METHODS = "view_v1_contact_methods";
151        public static final String PHONES = "view_v1_phones";
152        public static final String EXTENSIONS = "view_v1_extensions";
153        public static final String GROUP_MEMBERSHIP = "view_v1_group_membership";
154        public static final String PHOTOS = "view_v1_photos";
155        public static final String PRESENCE_JOIN_CONTACTS = Tables.PRESENCE +
156                " LEFT OUTER JOIN " + Tables.RAW_CONTACTS
157                + " ON (" + Tables.PRESENCE + "." + Presence.RAW_CONTACT_ID + "="
158                + RawContactsColumns.CONCRETE_ID + ")";
159    }
160
161    private static final String[] ORGANIZATION_MIME_TYPES = new String[] {
162        Organization.CONTENT_ITEM_TYPE
163    };
164
165    private static final String[] CONTACT_METHOD_MIME_TYPES = new String[] {
166        Email.CONTENT_ITEM_TYPE,
167        Im.CONTENT_ITEM_TYPE,
168        StructuredPostal.CONTENT_ITEM_TYPE,
169    };
170
171    private static final String[] PHONE_MIME_TYPES = new String[] {
172        Phone.CONTENT_ITEM_TYPE
173    };
174
175    private interface PhotoQuery {
176        String[] COLUMNS = { Data._ID };
177
178        int _ID = 0;
179    }
180
181    /**
182     * A custom data row that is used to store legacy photo data fields no
183     * longer directly supported by the API.
184     */
185    private interface LegacyPhotoData {
186        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo_v1_extras";
187
188        public static final String PHOTO_DATA_ID = Data.DATA1;
189        public static final String LOCAL_VERSION = Data.DATA2;
190        public static final String DOWNLOAD_REQUIRED = Data.DATA3;
191        public static final String EXISTS_ON_SERVER = Data.DATA4;
192        public static final String SYNC_ERROR = Data.DATA5;
193    }
194
195    public static final String LEGACY_PHOTO_JOIN =
196            " LEFT OUTER JOIN data legacy_photo ON (raw_contacts._id = legacy_photo.raw_contact_id"
197            + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = legacy_photo.mimetype_id)"
198                + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'"
199            + " AND " + DataColumns.CONCRETE_ID + " = legacy_photo." + LegacyPhotoData.PHOTO_DATA_ID
200            + ")";
201
202    private static final HashMap<String, String> sPeopleProjectionMap;
203    private static final HashMap<String, String> sOrganizationProjectionMap;
204    private static final HashMap<String, String> sContactMethodProjectionMap;
205    private static final HashMap<String, String> sPhoneProjectionMap;
206    private static final HashMap<String, String> sExtensionProjectionMap;
207    private static final HashMap<String, String> sGroupProjectionMap;
208    private static final HashMap<String, String> sGroupMembershipProjectionMap;
209    private static final HashMap<String, String> sPhotoProjectionMap;
210    private static final HashMap<String, String> sPresenceProjectionMap;
211
212    static {
213
214        // Contacts URI matching table
215        UriMatcher matcher = sUriMatcher;
216
217        String authority = android.provider.Contacts.AUTHORITY;
218        matcher.addURI(authority, "extensions", EXTENSIONS);
219        matcher.addURI(authority, "extensions/#", EXTENSIONS_ID);
220        matcher.addURI(authority, "groups", GROUPS);
221        matcher.addURI(authority, "groups/#", GROUPS_ID);
222//        matcher.addURI(authority, "groups/name/*/members", GROUP_NAME_MEMBERS);
223//        matcher.addURI(authority, "groups/name/*/members/filter/*",
224//                GROUP_NAME_MEMBERS_FILTER);
225//        matcher.addURI(authority, "groups/system_id/*/members", GROUP_SYSTEM_ID_MEMBERS);
226//        matcher.addURI(authority, "groups/system_id/*/members/filter/*",
227//                GROUP_SYSTEM_ID_MEMBERS_FILTER);
228        matcher.addURI(authority, "groupmembership", GROUPMEMBERSHIP);
229        matcher.addURI(authority, "groupmembership/#", GROUPMEMBERSHIP_ID);
230//        matcher.addURI(authority, "groupmembershipraw", GROUPMEMBERSHIP_RAW);
231        matcher.addURI(authority, "people", PEOPLE);
232//        matcher.addURI(authority, "people/strequent", PEOPLE_STREQUENT);
233//        matcher.addURI(authority, "people/strequent/filter/*", PEOPLE_STREQUENT_FILTER);
234        matcher.addURI(authority, "people/filter/*", PEOPLE_FILTER);
235//        matcher.addURI(authority, "people/with_phones_filter/*",
236//                PEOPLE_WITH_PHONES_FILTER);
237//        matcher.addURI(authority, "people/with_email_or_im_filter/*",
238//                PEOPLE_WITH_EMAIL_OR_IM_FILTER);
239        matcher.addURI(authority, "people/#", PEOPLE_ID);
240        matcher.addURI(authority, "people/#/extensions", PEOPLE_EXTENSIONS);
241        matcher.addURI(authority, "people/#/extensions/#", PEOPLE_EXTENSIONS_ID);
242        matcher.addURI(authority, "people/#/phones", PEOPLE_PHONES);
243        matcher.addURI(authority, "people/#/phones/#", PEOPLE_PHONES_ID);
244//        matcher.addURI(authority, "people/#/phones_with_presence",
245//                PEOPLE_PHONES_WITH_PRESENCE);
246        matcher.addURI(authority, "people/#/photo", PEOPLE_PHOTO);
247//        matcher.addURI(authority, "people/#/photo/data", PEOPLE_PHOTO_DATA);
248        matcher.addURI(authority, "people/#/contact_methods", PEOPLE_CONTACTMETHODS);
249//        matcher.addURI(authority, "people/#/contact_methods_with_presence",
250//                PEOPLE_CONTACTMETHODS_WITH_PRESENCE);
251        matcher.addURI(authority, "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
252//        matcher.addURI(authority, "people/#/organizations", PEOPLE_ORGANIZATIONS);
253//        matcher.addURI(authority, "people/#/organizations/#", PEOPLE_ORGANIZATIONS_ID);
254        matcher.addURI(authority, "people/#/groupmembership", PEOPLE_GROUPMEMBERSHIP);
255        matcher.addURI(authority, "people/#/groupmembership/#", PEOPLE_GROUPMEMBERSHIP_ID);
256//        matcher.addURI(authority, "people/raw", PEOPLE_RAW);
257//        matcher.addURI(authority, "people/owner", PEOPLE_OWNER);
258        matcher.addURI(authority, "people/#/update_contact_time",
259                PEOPLE_UPDATE_CONTACT_TIME);
260        matcher.addURI(authority, "deleted_people", DELETED_PEOPLE);
261        matcher.addURI(authority, "deleted_groups", DELETED_GROUPS);
262        matcher.addURI(authority, "phones", PHONES);
263//        matcher.addURI(authority, "phones_with_presence", PHONES_WITH_PRESENCE);
264//        matcher.addURI(authority, "phones/filter/*", PHONES_FILTER);
265//        matcher.addURI(authority, "phones/filter_name/*", PHONES_FILTER_NAME);
266//        matcher.addURI(authority, "phones/mobile_filter_name/*",
267//                PHONES_MOBILE_FILTER_NAME);
268        matcher.addURI(authority, "phones/#", PHONES_ID);
269        matcher.addURI(authority, "photos", PHOTOS);
270        matcher.addURI(authority, "photos/#", PHOTOS_ID);
271        matcher.addURI(authority, "contact_methods", CONTACTMETHODS);
272//        matcher.addURI(authority, "contact_methods/email", CONTACTMETHODS_EMAIL);
273//        matcher.addURI(authority, "contact_methods/email/*", CONTACTMETHODS_EMAIL_FILTER);
274        matcher.addURI(authority, "contact_methods/#", CONTACTMETHODS_ID);
275//        matcher.addURI(authority, "contact_methods/with_presence",
276//                CONTACTMETHODS_WITH_PRESENCE);
277        matcher.addURI(authority, "presence", PRESENCE);
278        matcher.addURI(authority, "presence/#", PRESENCE_ID);
279        matcher.addURI(authority, "organizations", ORGANIZATIONS);
280        matcher.addURI(authority, "organizations/#", ORGANIZATIONS_ID);
281//        matcher.addURI(authority, "voice_dialer_timestamp", VOICE_DIALER_TIMESTAMP);
282        matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY,
283                SEARCH_SUGGESTIONS);
284        matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
285                SEARCH_SUGGESTIONS);
286//        matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/#",
287//                SEARCH_SHORTCUT);
288//        matcher.addURI(authority, "settings", SETTINGS);
289//
290//        matcher.addURI(authority, "live_folders/people", LIVE_FOLDERS_PEOPLE);
291//        matcher.addURI(authority, "live_folders/people/*",
292//                LIVE_FOLDERS_PEOPLE_GROUP_NAME);
293//        matcher.addURI(authority, "live_folders/people_with_phones",
294//                LIVE_FOLDERS_PEOPLE_WITH_PHONES);
295//        matcher.addURI(authority, "live_folders/favorites",
296//                LIVE_FOLDERS_PEOPLE_FAVORITES);
297
298
299        HashMap<String, String> peopleProjectionMap = new HashMap<String, String>();
300        peopleProjectionMap.put(People.NAME, People.NAME);
301        peopleProjectionMap.put(People.DISPLAY_NAME, People.DISPLAY_NAME);
302        peopleProjectionMap.put(People.PHONETIC_NAME, People.PHONETIC_NAME);
303        peopleProjectionMap.put(People.NOTES, People.NOTES);
304        peopleProjectionMap.put(People.TIMES_CONTACTED, People.TIMES_CONTACTED);
305        peopleProjectionMap.put(People.LAST_TIME_CONTACTED, People.LAST_TIME_CONTACTED);
306        peopleProjectionMap.put(People.CUSTOM_RINGTONE, People.CUSTOM_RINGTONE);
307        peopleProjectionMap.put(People.SEND_TO_VOICEMAIL, People.SEND_TO_VOICEMAIL);
308        peopleProjectionMap.put(People.STARRED, People.STARRED);
309
310        sPeopleProjectionMap = new HashMap<String, String>(peopleProjectionMap);
311        sPeopleProjectionMap.put(People.PRIMARY_ORGANIZATION_ID, People.PRIMARY_ORGANIZATION_ID);
312        sPeopleProjectionMap.put(People.PRIMARY_EMAIL_ID, People.PRIMARY_EMAIL_ID);
313        sPeopleProjectionMap.put(People.PRIMARY_PHONE_ID, People.PRIMARY_PHONE_ID);
314        sPeopleProjectionMap.put(People.NUMBER, People.NUMBER);
315        sPeopleProjectionMap.put(People.TYPE, People.TYPE);
316        sPeopleProjectionMap.put(People.LABEL, People.LABEL);
317        sPeopleProjectionMap.put(People.NUMBER_KEY, People.NUMBER_KEY);
318        sPeopleProjectionMap.put(People.IM_PROTOCOL, People.IM_PROTOCOL);
319        sPeopleProjectionMap.put(People.IM_HANDLE, People.IM_HANDLE);
320        sPeopleProjectionMap.put(People.IM_ACCOUNT, People.IM_ACCOUNT);
321        sPeopleProjectionMap.put(People.PRESENCE_STATUS, People.PRESENCE_STATUS);
322        sPeopleProjectionMap.put(People.PRESENCE_CUSTOM_STATUS, People.PRESENCE_CUSTOM_STATUS);
323
324        sOrganizationProjectionMap = new HashMap<String, String>();
325        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.PERSON_ID,
326                android.provider.Contacts.Organizations.PERSON_ID);
327        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.ISPRIMARY,
328                android.provider.Contacts.Organizations.ISPRIMARY);
329        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.COMPANY,
330                android.provider.Contacts.Organizations.COMPANY);
331        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TYPE,
332                android.provider.Contacts.Organizations.TYPE);
333        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.LABEL,
334                android.provider.Contacts.Organizations.LABEL);
335        sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TITLE,
336                android.provider.Contacts.Organizations.TITLE);
337
338        sContactMethodProjectionMap = new HashMap<String, String>(peopleProjectionMap);
339        sContactMethodProjectionMap.put(ContactMethods.PERSON_ID, ContactMethods.PERSON_ID);
340        sContactMethodProjectionMap.put(ContactMethods.KIND, ContactMethods.KIND);
341        sContactMethodProjectionMap.put(ContactMethods.ISPRIMARY, ContactMethods.ISPRIMARY);
342        sContactMethodProjectionMap.put(ContactMethods.TYPE, ContactMethods.TYPE);
343        sContactMethodProjectionMap.put(ContactMethods.DATA, ContactMethods.DATA);
344        sContactMethodProjectionMap.put(ContactMethods.LABEL, ContactMethods.LABEL);
345        sContactMethodProjectionMap.put(ContactMethods.AUX_DATA, ContactMethods.AUX_DATA);
346
347        sPhoneProjectionMap = new HashMap<String, String>(peopleProjectionMap);
348        sPhoneProjectionMap.put(android.provider.Contacts.Phones.PERSON_ID,
349                android.provider.Contacts.Phones.PERSON_ID);
350        sPhoneProjectionMap.put(android.provider.Contacts.Phones.ISPRIMARY,
351                android.provider.Contacts.Phones.ISPRIMARY);
352        sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER,
353                android.provider.Contacts.Phones.NUMBER);
354        sPhoneProjectionMap.put(android.provider.Contacts.Phones.TYPE,
355                android.provider.Contacts.Phones.TYPE);
356        sPhoneProjectionMap.put(android.provider.Contacts.Phones.LABEL,
357                android.provider.Contacts.Phones.LABEL);
358        sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER_KEY,
359                android.provider.Contacts.Phones.NUMBER_KEY);
360
361        sExtensionProjectionMap = new HashMap<String, String>();
362        sExtensionProjectionMap.put(android.provider.Contacts.Extensions.PERSON_ID,
363                android.provider.Contacts.Extensions.PERSON_ID);
364        sExtensionProjectionMap.put(android.provider.Contacts.Extensions.NAME,
365                android.provider.Contacts.Extensions.NAME);
366        sExtensionProjectionMap.put(android.provider.Contacts.Extensions.VALUE,
367                android.provider.Contacts.Extensions.VALUE);
368
369        sGroupProjectionMap = new HashMap<String, String>();
370        sGroupProjectionMap.put(android.provider.Contacts.Groups._ID,
371                android.provider.Contacts.Groups._ID);
372        sGroupProjectionMap.put(android.provider.Contacts.Groups.NAME,
373                android.provider.Contacts.Groups.NAME);
374        sGroupProjectionMap.put(android.provider.Contacts.Groups.NOTES,
375                android.provider.Contacts.Groups.NOTES);
376        sGroupProjectionMap.put(android.provider.Contacts.Groups.SYSTEM_ID,
377                android.provider.Contacts.Groups.SYSTEM_ID);
378
379        sGroupMembershipProjectionMap = new HashMap<String, String>();
380        sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.PERSON_ID,
381                android.provider.Contacts.GroupMembership.PERSON_ID);
382        sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.GROUP_ID,
383                android.provider.Contacts.GroupMembership.GROUP_ID);
384
385        sPhotoProjectionMap = new HashMap<String, String>();
386        sPhotoProjectionMap.put(android.provider.Contacts.Photos.PERSON_ID,
387                android.provider.Contacts.Photos.PERSON_ID);
388        sPhotoProjectionMap.put(android.provider.Contacts.Photos.DATA,
389                android.provider.Contacts.Photos.DATA);
390        sPhotoProjectionMap.put(android.provider.Contacts.Photos.LOCAL_VERSION,
391                android.provider.Contacts.Photos.LOCAL_VERSION);
392        sPhotoProjectionMap.put(android.provider.Contacts.Photos.DOWNLOAD_REQUIRED,
393                android.provider.Contacts.Photos.DOWNLOAD_REQUIRED);
394        sPhotoProjectionMap.put(android.provider.Contacts.Photos.EXISTS_ON_SERVER,
395                android.provider.Contacts.Photos.EXISTS_ON_SERVER);
396        sPhotoProjectionMap.put(android.provider.Contacts.Photos.SYNC_ERROR,
397                android.provider.Contacts.Photos.SYNC_ERROR);
398
399        sPresenceProjectionMap = new HashMap<String, String>();
400        sPresenceProjectionMap.put(android.provider.Contacts.Presence._ID,
401                Tables.PRESENCE + "." + Presence._ID
402                        + " AS " + android.provider.Contacts.Presence._ID);
403        sPresenceProjectionMap.put(android.provider.Contacts.Presence.PERSON_ID,
404                Tables.PRESENCE + "." + Presence.RAW_CONTACT_ID
405                        + " AS " + android.provider.Contacts.Presence.PERSON_ID);
406        sPresenceProjectionMap.put(android.provider.Contacts.Presence.IM_PROTOCOL,
407                Presence.IM_PROTOCOL
408                    + " AS " + android.provider.Contacts.Presence.IM_PROTOCOL);
409        sPresenceProjectionMap.put(android.provider.Contacts.Presence.IM_HANDLE,
410                Presence.IM_HANDLE
411                    + " AS " + android.provider.Contacts.Presence.IM_HANDLE);
412        sPresenceProjectionMap.put(android.provider.Contacts.Presence.IM_ACCOUNT,
413                Presence.IM_ACCOUNT
414                    + " AS " + android.provider.Contacts.Presence.IM_ACCOUNT);
415        sPresenceProjectionMap.put(android.provider.Contacts.Presence.PRESENCE_STATUS,
416                Presence.PRESENCE_STATUS
417                    + " AS " + android.provider.Contacts.Presence.PRESENCE_STATUS);
418        sPresenceProjectionMap.put(android.provider.Contacts.Presence.PRESENCE_CUSTOM_STATUS,
419                Presence.PRESENCE_CUSTOM_STATUS
420                    + " AS " + android.provider.Contacts.Presence.PRESENCE_CUSTOM_STATUS);
421    }
422
423    private final Context mContext;
424    private final OpenHelper mOpenHelper;
425    private final ContactsProvider2 mContactsProvider;
426    private final NameSplitter mPhoneticNameSplitter;
427    private final GlobalSearchSupport mGlobalSearchSupport;
428
429    /** Precompiled sql statement for incrementing times contacted for a contact */
430    private final SQLiteStatement mLastTimeContactedUpdate;
431
432    private final ContentValues mValues = new ContentValues();
433    private Account mAccount;
434
435    public LegacyApiSupport(Context context, OpenHelper openHelper,
436            ContactsProvider2 contactsProvider, GlobalSearchSupport globalSearchSupport) {
437        mContext = context;
438        mContactsProvider = contactsProvider;
439        mOpenHelper = openHelper;
440        mGlobalSearchSupport = globalSearchSupport;
441        mOpenHelper.setDelegate(this);
442
443        mPhoneticNameSplitter = new NameSplitter("", "", "",
444                context.getString(com.android.internal.R.string.common_name_conjunctions));
445
446        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
447        mLastTimeContactedUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET "
448                + RawContacts.TIMES_CONTACTED + "="
449                + RawContacts.TIMES_CONTACTED + "+1,"
450                + RawContacts.LAST_TIME_CONTACTED + "=? WHERE "
451                + RawContacts._ID + "=?");
452    }
453
454    private void ensureDefaultAccount() {
455        if (mAccount == null) {
456            mAccount = mContactsProvider.getDefaultAccount();
457        }
458    }
459
460    public void createDatabase(SQLiteDatabase db) {
461
462        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PEOPLE + ";");
463        db.execSQL("CREATE VIEW " + LegacyTables.PEOPLE + " AS SELECT " +
464                RawContactsColumns.CONCRETE_ID
465                        + " AS " + android.provider.Contacts.People._ID + ", " +
466                "name." + StructuredName.DISPLAY_NAME
467                        + " AS " + People.NAME + ", " +
468                Tables.RAW_CONTACTS + "." + RawContactsColumns.DISPLAY_NAME
469                        + " AS " + People.DISPLAY_NAME + ", " +
470                PHONETIC_NAME_SQL
471                        + " AS " + People.PHONETIC_NAME + " , " +
472                "note." + Note.NOTE
473                        + " AS " + People.NOTES + ", " +
474                RawContacts.ACCOUNT_NAME + ", " +
475                RawContacts.ACCOUNT_TYPE + ", " +
476                Tables.RAW_CONTACTS + "." + RawContacts.TIMES_CONTACTED
477                        + " AS " + People.TIMES_CONTACTED + ", " +
478                Tables.RAW_CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED
479                        + " AS " + People.LAST_TIME_CONTACTED + ", " +
480                Tables.RAW_CONTACTS + "." + RawContacts.CUSTOM_RINGTONE
481                        + " AS " + People.CUSTOM_RINGTONE + ", " +
482                Tables.RAW_CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL
483                        + " AS " + People.SEND_TO_VOICEMAIL + ", " +
484                Tables.RAW_CONTACTS + "." + RawContacts.STARRED
485                        + " AS " + People.STARRED + ", " +
486                "organization." + Data._ID
487                        + " AS " + People.PRIMARY_ORGANIZATION_ID + ", " +
488                "email." + Data._ID
489                        + " AS " + People.PRIMARY_EMAIL_ID + ", " +
490                "phone." + Data._ID
491                        + " AS " + People.PRIMARY_PHONE_ID + ", " +
492                "phone." + Phone.NUMBER
493                        + " AS " + People.NUMBER + ", " +
494                "phone." + Phone.TYPE
495                        + " AS " + People.TYPE + ", " +
496                "phone." + Phone.LABEL
497                        + " AS " + People.LABEL + ", " +
498                "phone." + PhoneColumns.NORMALIZED_NUMBER
499                        + " AS " + People.NUMBER_KEY +
500                " FROM " + Tables.RAW_CONTACTS + PEOPLE_JOINS +
501                " WHERE " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
502                        + " AND " + RawContacts.IS_RESTRICTED + "=0" +
503        ";");
504
505        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.ORGANIZATIONS + ";");
506        db.execSQL("CREATE VIEW " + LegacyTables.ORGANIZATIONS + " AS SELECT " +
507                DataColumns.CONCRETE_ID
508                        + " AS " + android.provider.Contacts.Organizations._ID + ", " +
509                Data.RAW_CONTACT_ID
510                        + " AS " + android.provider.Contacts.Organizations.PERSON_ID + ", " +
511                Data.IS_PRIMARY
512                        + " AS " + android.provider.Contacts.Organizations.ISPRIMARY + ", " +
513                RawContacts.ACCOUNT_NAME + ", " +
514                RawContacts.ACCOUNT_TYPE + ", " +
515                Organization.COMPANY
516                        + " AS " + android.provider.Contacts.Organizations.COMPANY + ", " +
517                Organization.TYPE
518                        + " AS " + android.provider.Contacts.Organizations.TYPE + ", " +
519                Organization.LABEL
520                        + " AS " + android.provider.Contacts.Organizations.LABEL + ", " +
521                Organization.TITLE
522                        + " AS " + android.provider.Contacts.Organizations.TITLE +
523                " FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
524                " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
525                        + Organization.CONTENT_ITEM_TYPE + "'"
526                        + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
527                        + " AND " + RawContacts.IS_RESTRICTED + "=0" +
528        ";");
529
530        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.CONTACT_METHODS + ";");
531        db.execSQL("CREATE VIEW " + LegacyTables.CONTACT_METHODS + " AS SELECT " +
532                DataColumns.CONCRETE_ID
533                        + " AS " + ContactMethods._ID + ", " +
534                DataColumns.CONCRETE_RAW_CONTACT_ID
535                        + " AS " + ContactMethods.PERSON_ID + ", " +
536                CONTACT_METHOD_KIND_SQL
537                        + " AS " + ContactMethods.KIND + ", " +
538                DataColumns.CONCRETE_IS_PRIMARY
539                        + " AS " + ContactMethods.ISPRIMARY + ", " +
540                DataColumns.CONCRETE_DATA1
541                        + " AS " + ContactMethods.TYPE + ", " +
542                DataColumns.CONCRETE_DATA2
543                        + " AS " + ContactMethods.DATA + ", " +
544                DataColumns.CONCRETE_DATA3
545                        + " AS " + ContactMethods.LABEL + ", " +
546                DataColumns.CONCRETE_DATA14
547                        + " AS " + ContactMethods.AUX_DATA + ", " +
548                "name." + StructuredName.DISPLAY_NAME
549                        + " AS " + ContactMethods.NAME + ", " +
550                Tables.RAW_CONTACTS + "." + RawContactsColumns.DISPLAY_NAME
551                        + " AS " + ContactMethods.DISPLAY_NAME + ", " +
552                RawContacts.ACCOUNT_NAME + ", " +
553                RawContacts.ACCOUNT_TYPE + ", " +
554                PHONETIC_NAME_SQL
555                        + " AS " + ContactMethods.PHONETIC_NAME + " , " +
556                "note." + Note.NOTE
557                        + " AS " + ContactMethods.NOTES + ", " +
558                Tables.RAW_CONTACTS + "." + RawContacts.TIMES_CONTACTED
559                        + " AS " + ContactMethods.TIMES_CONTACTED + ", " +
560                Tables.RAW_CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED
561                        + " AS " + ContactMethods.LAST_TIME_CONTACTED + ", " +
562                Tables.RAW_CONTACTS + "." + RawContacts.CUSTOM_RINGTONE
563                        + " AS " + ContactMethods.CUSTOM_RINGTONE + ", " +
564                Tables.RAW_CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL
565                        + " AS " + ContactMethods.SEND_TO_VOICEMAIL + ", " +
566                Tables.RAW_CONTACTS + "." + RawContacts.STARRED
567                        + " AS " + ContactMethods.STARRED +
568                " FROM " + Tables.DATA + DATA_JOINS +
569                " WHERE " + ContactMethods.KIND + " IS NOT NULL"
570                    + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
571                    + " AND " + RawContacts.IS_RESTRICTED + "=0" +
572        ";");
573
574
575        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHONES + ";");
576        db.execSQL("CREATE VIEW " + LegacyTables.PHONES + " AS SELECT " +
577                DataColumns.CONCRETE_ID
578                        + " AS " + android.provider.Contacts.Phones._ID + ", " +
579                DataColumns.CONCRETE_RAW_CONTACT_ID
580                        + " AS " + android.provider.Contacts.Phones.PERSON_ID + ", " +
581                DataColumns.CONCRETE_IS_PRIMARY
582                        + " AS " + android.provider.Contacts.Phones.ISPRIMARY + ", " +
583                Tables.DATA + "." + Phone.NUMBER
584                        + " AS " + android.provider.Contacts.Phones.NUMBER + ", " +
585                Tables.DATA + "." + Phone.TYPE
586                        + " AS " + android.provider.Contacts.Phones.TYPE + ", " +
587                Tables.DATA + "." + Phone.LABEL
588                        + " AS " + android.provider.Contacts.Phones.LABEL + ", " +
589                PhoneColumns.CONCRETE_NORMALIZED_NUMBER
590                        + " AS " + android.provider.Contacts.Phones.NUMBER_KEY + ", " +
591                "name." + StructuredName.DISPLAY_NAME
592                        + " AS " + android.provider.Contacts.Phones.NAME + ", " +
593                Tables.RAW_CONTACTS + "." + RawContactsColumns.DISPLAY_NAME
594                        + " AS " + android.provider.Contacts.Phones.DISPLAY_NAME + ", " +
595                RawContacts.ACCOUNT_NAME + ", " +
596                RawContacts.ACCOUNT_TYPE + ", " +
597                PHONETIC_NAME_SQL
598                        + " AS " + android.provider.Contacts.Phones.PHONETIC_NAME + " , " +
599                "note." + Note.NOTE
600                        + " AS " + android.provider.Contacts.Phones.NOTES + ", " +
601                Tables.RAW_CONTACTS + "." + RawContacts.TIMES_CONTACTED
602                        + " AS " + android.provider.Contacts.Phones.TIMES_CONTACTED + ", " +
603                Tables.RAW_CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED
604                        + " AS " + android.provider.Contacts.Phones.LAST_TIME_CONTACTED + ", " +
605                Tables.RAW_CONTACTS + "." + RawContacts.CUSTOM_RINGTONE
606                        + " AS " + android.provider.Contacts.Phones.CUSTOM_RINGTONE + ", " +
607                Tables.RAW_CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL
608                        + " AS " + android.provider.Contacts.Phones.SEND_TO_VOICEMAIL + ", " +
609                Tables.RAW_CONTACTS + "." + RawContacts.STARRED
610                        + " AS " + android.provider.Contacts.Phones.STARRED +
611                " FROM " + Tables.DATA + DATA_JOINS +
612                " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
613                        + Phone.CONTENT_ITEM_TYPE + "'"
614                        + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
615                        + " AND " + RawContacts.IS_RESTRICTED + "=0" +
616        ";");
617
618        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.EXTENSIONS + ";");
619        db.execSQL("CREATE VIEW " + LegacyTables.EXTENSIONS + " AS SELECT " +
620                DataColumns.CONCRETE_ID
621                        + " AS " + android.provider.Contacts.Extensions._ID + ", " +
622                DataColumns.CONCRETE_RAW_CONTACT_ID
623                        + " AS " + android.provider.Contacts.Extensions.PERSON_ID + ", " +
624                RawContacts.ACCOUNT_NAME + ", " +
625                RawContacts.ACCOUNT_TYPE + ", " +
626                ExtensionsColumns.NAME
627                        + " AS " + android.provider.Contacts.Extensions.NAME + ", " +
628                ExtensionsColumns.VALUE
629                        + " AS " + android.provider.Contacts.Extensions.VALUE +
630                " FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
631                " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
632                        + android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE + "'"
633                        + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
634                        + " AND " + RawContacts.IS_RESTRICTED + "=0" +
635        ";");
636
637        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUPS + ";");
638        db.execSQL("CREATE VIEW " + LegacyTables.GROUPS + " AS SELECT " +
639                GroupsColumns.CONCRETE_ID + " AS " + android.provider.Contacts.Groups._ID + ", " +
640                Groups.ACCOUNT_NAME + ", " +
641                Groups.ACCOUNT_TYPE + ", " +
642                Groups.TITLE + " AS " + android.provider.Contacts.Groups.NAME + ", " +
643                Groups.NOTES + " AS " + android.provider.Contacts.Groups.NOTES + " , " +
644                Groups.SYSTEM_ID + " AS " + android.provider.Contacts.Groups.SYSTEM_ID +
645                " FROM " + Tables.GROUPS +
646        ";");
647
648        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUP_MEMBERSHIP + ";");
649        db.execSQL("CREATE VIEW " + LegacyTables.GROUP_MEMBERSHIP + " AS SELECT " +
650                DataColumns.CONCRETE_ID
651                        + " AS " + android.provider.Contacts.GroupMembership._ID + ", " +
652                DataColumns.CONCRETE_RAW_CONTACT_ID
653                        + " AS " + android.provider.Contacts.GroupMembership.PERSON_ID + ", " +
654                Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_NAME
655                        + " AS " +  RawContacts.ACCOUNT_NAME + ", " +
656                Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_TYPE
657                        + " AS " +  RawContacts.ACCOUNT_TYPE + ", " +
658                GroupMembership.GROUP_ROW_ID
659                        + " AS " + android.provider.Contacts.GroupMembership.GROUP_ID + ", " +
660                Groups.TITLE
661                        + " AS " + android.provider.Contacts.GroupMembership.NAME + ", " +
662                Groups.NOTES
663                        + " AS " + android.provider.Contacts.GroupMembership.NOTES + " , " +
664                Groups.SYSTEM_ID
665                        + " AS " + android.provider.Contacts.GroupMembership.SYSTEM_ID +
666                " FROM " + Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS +
667                " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
668                        + GroupMembership.CONTENT_ITEM_TYPE + "'"
669                        + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
670        ";");
671
672        db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHOTOS + ";");
673        db.execSQL("CREATE VIEW " + LegacyTables.PHOTOS + " AS SELECT " +
674                DataColumns.CONCRETE_ID
675                        + " AS " + android.provider.Contacts.Photos._ID + ", " +
676                DataColumns.CONCRETE_RAW_CONTACT_ID
677                        + " AS " + android.provider.Contacts.Photos.PERSON_ID + ", " +
678                RawContacts.ACCOUNT_NAME + ", " +
679                RawContacts.ACCOUNT_TYPE + ", " +
680                Tables.DATA + "." + Photo.PHOTO
681                        + " AS " + android.provider.Contacts.Photos.DATA + ", " +
682                "legacy_photo." + LegacyPhotoData.EXISTS_ON_SERVER
683                        + " AS " + android.provider.Contacts.Photos.EXISTS_ON_SERVER + ", " +
684                "legacy_photo." + LegacyPhotoData.DOWNLOAD_REQUIRED
685                        + " AS " + android.provider.Contacts.Photos.DOWNLOAD_REQUIRED + ", " +
686                "legacy_photo." + LegacyPhotoData.LOCAL_VERSION
687                        + " AS " + android.provider.Contacts.Photos.LOCAL_VERSION + ", " +
688                "legacy_photo." + LegacyPhotoData.SYNC_ERROR
689                        + " AS " + android.provider.Contacts.Photos.SYNC_ERROR +
690                " FROM " + Tables.DATA + DATA_JOINS + LEGACY_PHOTO_JOIN +
691                " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
692                        + Photo.CONTENT_ITEM_TYPE + "'"
693                        + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0"
694                        + " AND " + RawContacts.IS_RESTRICTED + "=0" +
695        ";");
696    }
697
698    public Uri insert(Uri uri, ContentValues values) {
699        final int match = sUriMatcher.match(uri);
700        long id = 0;
701        switch (match) {
702            case PEOPLE:
703                id = insertPeople(values);
704                break;
705
706            case ORGANIZATIONS:
707                id = insertOrganization(values);
708                break;
709
710            case PEOPLE_CONTACTMETHODS: {
711                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
712                id = insertContactMethod(rawContactId, values);
713                break;
714            }
715
716            case CONTACTMETHODS: {
717                long rawContactId = getRequiredValue(values, ContactMethods.PERSON_ID);
718                id = insertContactMethod(rawContactId, values);
719                break;
720            }
721
722            case PHONES: {
723                long rawContactId = getRequiredValue(values,
724                        android.provider.Contacts.Phones.PERSON_ID);
725                id = insertPhone(rawContactId, values);
726                break;
727            }
728
729            case EXTENSIONS: {
730                long rawContactId = getRequiredValue(values,
731                        android.provider.Contacts.Extensions.PERSON_ID);
732                id = insertExtension(rawContactId, values);
733                break;
734            }
735
736            case GROUPS:
737                id = insertGroup(values);
738                break;
739
740            case GROUPMEMBERSHIP: {
741                long rawContactId = getRequiredValue(values,
742                        android.provider.Contacts.GroupMembership.PERSON_ID);
743                long groupId = getRequiredValue(values,
744                        android.provider.Contacts.GroupMembership.GROUP_ID);
745                id = insertGroupMembership(rawContactId, groupId);
746                break;
747            }
748
749            case PRESENCE: {
750                id = insertPresence(values);
751                break;
752            }
753
754            default:
755                throw new UnsupportedOperationException("Unknown uri: " + uri);
756        }
757
758        if (id < 0) {
759            return null;
760        }
761
762        final Uri result = ContentUris.withAppendedId(uri, id);
763        onChange(result);
764        return result;
765    }
766
767    private long getRequiredValue(ContentValues values, String column) {
768        if (!values.containsKey(column)) {
769            throw new RuntimeException("Required value: " + column);
770        }
771
772        return values.getAsLong(column);
773    }
774
775    private long insertPeople(ContentValues values) {
776        ensureDefaultAccount();
777
778        mValues.clear();
779
780        OpenHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
781                values, People.CUSTOM_RINGTONE);
782        OpenHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
783                values, People.SEND_TO_VOICEMAIL);
784        OpenHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
785                values, People.LAST_TIME_CONTACTED);
786        OpenHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
787                values, People.TIMES_CONTACTED);
788        OpenHelper.copyLongValue(mValues, RawContacts.STARRED,
789                values, People.STARRED);
790        mValues.put(RawContacts.ACCOUNT_NAME, mAccount.mName);
791        mValues.put(RawContacts.ACCOUNT_TYPE, mAccount.mType);
792        Uri contactUri = mContactsProvider.insert(RawContacts.CONTENT_URI, mValues);
793        long rawContactId = ContentUris.parseId(contactUri);
794
795        if (values.containsKey(People.NAME) || values.containsKey(People.PHONETIC_NAME)) {
796            mValues.clear();
797            mValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
798            mValues.put(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
799            OpenHelper.copyStringValue(mValues, StructuredName.DISPLAY_NAME,
800                    values, People.NAME);
801            if (values.containsKey(People.PHONETIC_NAME)) {
802                String phoneticName = values.getAsString(People.PHONETIC_NAME);
803                NameSplitter.Name parsedName = new NameSplitter.Name();
804                mPhoneticNameSplitter.split(parsedName, phoneticName);
805                mValues.put(StructuredName.PHONETIC_GIVEN_NAME, parsedName.getGivenNames());
806                mValues.put(StructuredName.PHONETIC_MIDDLE_NAME, parsedName.getMiddleName());
807                mValues.put(StructuredName.PHONETIC_FAMILY_NAME, parsedName.getFamilyName());
808            }
809
810            mContactsProvider.insert(ContactsContract.Data.CONTENT_URI, mValues);
811        }
812
813        if (values.containsKey(People.NOTES)) {
814            mValues.clear();
815            mValues.put(Data.RAW_CONTACT_ID, rawContactId);
816            mValues.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
817            OpenHelper.copyStringValue(mValues, Note.NOTE, values, People.NOTES);
818            mContactsProvider.insert(Data.CONTENT_URI, mValues);
819        }
820
821        // TODO instant aggregation
822        return rawContactId;
823    }
824
825    private long insertOrganization(ContentValues values) {
826        mValues.clear();
827
828        OpenHelper.copyLongValue(mValues, Data.RAW_CONTACT_ID,
829                values, android.provider.Contacts.Organizations.PERSON_ID);
830        mValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
831
832        OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY,
833                values, android.provider.Contacts.Organizations.ISPRIMARY);
834
835        OpenHelper.copyStringValue(mValues, Organization.COMPANY,
836                values, android.provider.Contacts.Organizations.COMPANY);
837
838        // TYPE values happen to remain the same between V1 and V2 - can just copy the value
839        OpenHelper.copyLongValue(mValues, Organization.TYPE,
840                values, android.provider.Contacts.Organizations.TYPE);
841
842        OpenHelper.copyStringValue(mValues, Organization.LABEL,
843                values, android.provider.Contacts.Organizations.LABEL);
844        OpenHelper.copyStringValue(mValues, Organization.TITLE,
845                values, android.provider.Contacts.Organizations.TITLE);
846
847        Uri uri = mContactsProvider.insert(Data.CONTENT_URI, mValues);
848
849        return ContentUris.parseId(uri);
850    }
851
852    private long insertPhone(long rawContactId, ContentValues values) {
853        mValues.clear();
854
855        mValues.put(Data.RAW_CONTACT_ID, rawContactId);
856        mValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
857
858        OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY,
859                values, android.provider.Contacts.Phones.ISPRIMARY);
860
861        OpenHelper.copyStringValue(mValues, Phone.NUMBER,
862                values, android.provider.Contacts.Phones.NUMBER);
863
864        // TYPE values happen to remain the same between V1 and V2 - can just copy the value
865        OpenHelper.copyLongValue(mValues, Phone.TYPE,
866                values, android.provider.Contacts.Phones.TYPE);
867
868        OpenHelper.copyStringValue(mValues, Phone.LABEL,
869                values, android.provider.Contacts.Phones.LABEL);
870
871        Uri uri = mContactsProvider.insert(Data.CONTENT_URI, mValues);
872
873        return ContentUris.parseId(uri);
874    }
875
876    private long insertContactMethod(long rawContactId, ContentValues values) {
877        Integer kind = values.getAsInteger(ContactMethods.KIND);
878        if (kind == null) {
879            throw new RuntimeException("Required value: " + ContactMethods.KIND);
880        }
881
882        mValues.clear();
883        mValues.put(Data.RAW_CONTACT_ID, rawContactId);
884
885        OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY, values, ContactMethods.ISPRIMARY);
886
887        switch (kind) {
888            case android.provider.Contacts.KIND_EMAIL:
889                copyCommonFields(values, Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL,
890                        Email.DATA, Data.DATA14);
891                break;
892
893            case android.provider.Contacts.KIND_IM:
894                copyCommonFields(values, Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL,
895                        Email.DATA, Data.DATA14);
896                break;
897
898            case android.provider.Contacts.KIND_POSTAL:
899                copyCommonFields(values, StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE,
900                        StructuredPostal.LABEL, StructuredPostal.FORMATTED_ADDRESS, Data.DATA14);
901                break;
902        }
903
904        Uri uri = mContactsProvider.insert(Data.CONTENT_URI, mValues);
905        return ContentUris.parseId(uri);
906    }
907
908    private void copyCommonFields(ContentValues values, String mimeType, String typeColumn,
909            String labelColumn, String dataColumn, String auxDataColumn) {
910        mValues.put(Data.MIMETYPE, mimeType);
911        OpenHelper.copyLongValue(mValues, typeColumn, values, ContactMethods.TYPE);
912        OpenHelper.copyStringValue(mValues, labelColumn, values, ContactMethods.LABEL);
913        OpenHelper.copyStringValue(mValues, dataColumn, values, ContactMethods.DATA);
914        OpenHelper.copyStringValue(mValues, auxDataColumn, values, ContactMethods.AUX_DATA);
915    }
916
917    private long insertExtension(long rawContactId, ContentValues values) {
918        mValues.clear();
919
920        mValues.put(Data.RAW_CONTACT_ID, rawContactId);
921        mValues.put(Data.MIMETYPE, android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE);
922
923        OpenHelper.copyStringValue(mValues, ExtensionsColumns.NAME,
924                values, android.provider.Contacts.People.Extensions.NAME);
925        OpenHelper.copyStringValue(mValues, ExtensionsColumns.VALUE,
926                values, android.provider.Contacts.People.Extensions.VALUE);
927
928        Uri uri = mContactsProvider.insert(Data.CONTENT_URI, mValues);
929        return ContentUris.parseId(uri);
930    }
931
932    private long insertGroup(ContentValues values) {
933        ensureDefaultAccount();
934        mValues.clear();
935
936        OpenHelper.copyStringValue(mValues, Groups.TITLE,
937                values, android.provider.Contacts.Groups.NAME);
938        OpenHelper.copyStringValue(mValues, Groups.NOTES,
939                values, android.provider.Contacts.Groups.NOTES);
940        OpenHelper.copyStringValue(mValues, Groups.SYSTEM_ID,
941                values, android.provider.Contacts.Groups.SYSTEM_ID);
942
943        mValues.put(Groups.ACCOUNT_NAME, mAccount.mName);
944        mValues.put(Groups.ACCOUNT_TYPE, mAccount.mType);
945
946        Uri uri = mContactsProvider.insert(Groups.CONTENT_URI, mValues);
947        return ContentUris.parseId(uri);
948    }
949
950    private long insertGroupMembership(long rawContactId, long groupId) {
951        mValues.clear();
952
953        mValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
954        mValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
955        mValues.put(GroupMembership.GROUP_ROW_ID, groupId);
956
957        Uri uri = mContactsProvider.insert(Data.CONTENT_URI, mValues);
958        return ContentUris.parseId(uri);
959    }
960
961    private long insertPresence(ContentValues values) {
962        mValues.clear();
963
964        String protocol = values.getAsString(android.provider.Contacts.Presence.IM_PROTOCOL);
965        if (protocol == null) {
966            throw new IllegalArgumentException("IM_PROTOCOL is required");
967        }
968
969        if (protocol.startsWith("pre:")) {
970            mValues.put(Presence.IM_PROTOCOL, Integer.parseInt(protocol.substring(4)));
971        } else if (protocol.startsWith("custom:")) {
972            mValues.put(Presence.IM_PROTOCOL, Im.PROTOCOL_CUSTOM);
973            // TODO add support for custom protocol
974        }
975
976        OpenHelper.copyLongValue(mValues, Presence._ID,
977                values, android.provider.Contacts.Presence._ID);
978        OpenHelper.copyLongValue(mValues, Presence.RAW_CONTACT_ID,
979                values, android.provider.Contacts.Presence.PERSON_ID);
980        OpenHelper.copyStringValue(mValues, Presence.IM_HANDLE,
981                values, android.provider.Contacts.Presence.IM_HANDLE);
982        OpenHelper.copyStringValue(mValues, Presence.IM_ACCOUNT,
983                values, android.provider.Contacts.Presence.IM_ACCOUNT);
984        OpenHelper.copyLongValue(mValues, Presence.PRESENCE_STATUS,
985                values, android.provider.Contacts.Presence.PRESENCE_STATUS);
986        OpenHelper.copyStringValue(mValues, Presence.PRESENCE_CUSTOM_STATUS,
987                values, android.provider.Contacts.Presence.PRESENCE_CUSTOM_STATUS);
988
989        return mContactsProvider.insertPresence(mValues);
990    }
991
992    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
993        final int match = sUriMatcher.match(uri);
994        int count = 0;
995        switch(match) {
996            case PEOPLE_UPDATE_CONTACT_TIME:
997                count = updateContactTime(uri, values);
998                break;
999
1000            case PEOPLE_PHOTO: {
1001                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
1002                return updatePhoto(rawContactId, values);
1003            }
1004
1005            case PHOTOS:
1006                // TODO
1007                break;
1008
1009            case PHOTOS_ID:
1010                // TODO
1011                break;
1012
1013
1014            default:
1015                throw new UnsupportedOperationException("Unknown uri: " + uri);
1016        }
1017
1018        if (count > 0) {
1019            mContext.getContentResolver().notifyChange(uri, null);
1020        }
1021        return count;
1022    }
1023
1024
1025    private int updateContactTime(Uri uri, ContentValues values) {
1026
1027        // TODO check sanctions
1028
1029        long lastTimeContacted;
1030        if (values.containsKey(People.LAST_TIME_CONTACTED)) {
1031            lastTimeContacted = values.getAsLong(People.LAST_TIME_CONTACTED);
1032        } else {
1033            lastTimeContacted = System.currentTimeMillis();
1034        }
1035
1036        long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
1037        long contactId = mOpenHelper.getContactId(rawContactId);
1038        if (contactId != 0) {
1039            mContactsProvider.updateContactTime(contactId, lastTimeContacted);
1040        } else {
1041            mLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
1042            mLastTimeContactedUpdate.bindLong(2, rawContactId);
1043            mLastTimeContactedUpdate.execute();
1044        }
1045        return 1;
1046    }
1047
1048    private int updatePhoto(long rawContactId, ContentValues values) {
1049
1050        // TODO check sanctions
1051
1052        int count;
1053
1054        long dataId = -1;
1055        Cursor c = mContactsProvider.query(Data.CONTENT_URI, PhotoQuery.COLUMNS,
1056                Data.RAW_CONTACT_ID + "=" + rawContactId + " AND "
1057                        + Data.MIMETYPE + "=" + mOpenHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE),
1058                null, null);
1059        try {
1060            if (c.moveToFirst()) {
1061                dataId = c.getLong(PhotoQuery._ID);
1062            }
1063        } finally {
1064            c.close();
1065        }
1066
1067        mValues.clear();
1068        byte[] bytes = values.getAsByteArray(android.provider.Contacts.Photos.DATA);
1069        mValues.put(Photo.PHOTO, bytes);
1070
1071        if (dataId == -1) {
1072            mValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
1073            mValues.put(Data.RAW_CONTACT_ID, rawContactId);
1074            Uri dataUri = mContactsProvider.insert(Data.CONTENT_URI, mValues);
1075            dataId = ContentUris.parseId(dataUri);
1076            count = 1;
1077        } else {
1078            Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
1079            count = mContactsProvider.update(dataUri, mValues, null, null);
1080        }
1081
1082        mValues.clear();
1083        OpenHelper.copyStringValue(mValues, LegacyPhotoData.LOCAL_VERSION,
1084                values, android.provider.Contacts.Photos.LOCAL_VERSION);
1085        OpenHelper.copyStringValue(mValues, LegacyPhotoData.DOWNLOAD_REQUIRED,
1086                values, android.provider.Contacts.Photos.DOWNLOAD_REQUIRED);
1087        OpenHelper.copyStringValue(mValues, LegacyPhotoData.EXISTS_ON_SERVER,
1088                values, android.provider.Contacts.Photos.EXISTS_ON_SERVER);
1089        OpenHelper.copyStringValue(mValues, LegacyPhotoData.SYNC_ERROR,
1090                values, android.provider.Contacts.Photos.SYNC_ERROR);
1091
1092        int updated = mContactsProvider.update(Data.CONTENT_URI, mValues,
1093                Data.MIMETYPE + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'"
1094                        + " AND " + Data.RAW_CONTACT_ID + "=" + rawContactId
1095                        + " AND " + LegacyPhotoData.PHOTO_DATA_ID + "=" + dataId, null);
1096        if (updated == 0) {
1097            mValues.put(Data.RAW_CONTACT_ID, rawContactId);
1098            mValues.put(Data.MIMETYPE, LegacyPhotoData.CONTENT_ITEM_TYPE);
1099            mValues.put(LegacyPhotoData.PHOTO_DATA_ID, dataId);
1100            mContactsProvider.insert(Data.CONTENT_URI, mValues);
1101        }
1102
1103        return count;
1104    }
1105
1106    public int delete(Uri uri, String selection, String[] selectionArgs) {
1107        final int match = sUriMatcher.match(uri);
1108        int count = 0;
1109        switch (match) {
1110            case PEOPLE_ID:
1111                count = mContactsProvider.deleteRawContact(ContentUris.parseId(uri), false);
1112                break;
1113
1114            case ORGANIZATIONS_ID:
1115                count = mContactsProvider.deleteData(ContentUris.parseId(uri),
1116                        ORGANIZATION_MIME_TYPES);
1117                break;
1118
1119            case CONTACTMETHODS_ID:
1120                count = mContactsProvider.deleteData(ContentUris.parseId(uri),
1121                        CONTACT_METHOD_MIME_TYPES);
1122                break;
1123
1124            case PHONES_ID:
1125                count = mContactsProvider.deleteData(ContentUris.parseId(uri),
1126                        PHONE_MIME_TYPES);
1127                break;
1128
1129            default:
1130                throw new UnsupportedOperationException("Unknown uri: " + uri);
1131        }
1132
1133        return count;
1134    }
1135
1136    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
1137            String sortOrder, String limit) {
1138        ensureDefaultAccount();
1139
1140        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
1141        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1142        String groupBy = null;
1143
1144        final int match = sUriMatcher.match(uri);
1145        switch (match) {
1146            case PEOPLE: {
1147                qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1148                qb.setProjectionMap(sPeopleProjectionMap);
1149                applyRawContactsAccount(qb, uri);
1150                break;
1151            }
1152
1153            case PEOPLE_ID:
1154                qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1155                qb.setProjectionMap(sPeopleProjectionMap);
1156                applyRawContactsAccount(qb, uri);
1157                qb.appendWhere(" AND " + People._ID + "=");
1158                qb.appendWhere(uri.getPathSegments().get(1));
1159                break;
1160
1161            case PEOPLE_FILTER: {
1162                qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1163                qb.setProjectionMap(sPeopleProjectionMap);
1164                applyRawContactsAccount(qb, uri);
1165                String filterParam = uri.getPathSegments().get(2);
1166                qb.appendWhere(" AND " + People._ID + " IN "
1167                        + mContactsProvider.getRawContactsByFilterAsNestedQuery(filterParam));
1168                break;
1169            }
1170
1171            case ORGANIZATIONS:
1172                qb.setTables(LegacyTables.ORGANIZATIONS);
1173                qb.setProjectionMap(sOrganizationProjectionMap);
1174                applyRawContactsAccount(qb, uri);
1175                break;
1176
1177            case ORGANIZATIONS_ID:
1178                qb.setTables(LegacyTables.ORGANIZATIONS);
1179                qb.setProjectionMap(sOrganizationProjectionMap);
1180                applyRawContactsAccount(qb, uri);
1181                qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "=");
1182                qb.appendWhere(uri.getPathSegments().get(1));
1183                break;
1184
1185            case CONTACTMETHODS:
1186                qb.setTables(LegacyTables.CONTACT_METHODS);
1187                qb.setProjectionMap(sContactMethodProjectionMap);
1188                applyRawContactsAccount(qb, uri);
1189                break;
1190
1191            case CONTACTMETHODS_ID:
1192                qb.setTables(LegacyTables.CONTACT_METHODS);
1193                qb.setProjectionMap(sContactMethodProjectionMap);
1194                applyRawContactsAccount(qb, uri);
1195                qb.appendWhere(" AND " + ContactMethods._ID + "=");
1196                qb.appendWhere(uri.getPathSegments().get(1));
1197                break;
1198
1199            case PEOPLE_CONTACTMETHODS:
1200                qb.setTables(LegacyTables.CONTACT_METHODS);
1201                qb.setProjectionMap(sContactMethodProjectionMap);
1202                applyRawContactsAccount(qb, uri);
1203                qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "=");
1204                qb.appendWhere(uri.getPathSegments().get(1));
1205                qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL");
1206                break;
1207
1208            case PEOPLE_CONTACTMETHODS_ID:
1209                qb.setTables(LegacyTables.CONTACT_METHODS);
1210                qb.setProjectionMap(sContactMethodProjectionMap);
1211                applyRawContactsAccount(qb, uri);
1212                qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "=");
1213                qb.appendWhere(uri.getPathSegments().get(1));
1214                qb.appendWhere(" AND " + ContactMethods._ID + "=");
1215                qb.appendWhere(uri.getPathSegments().get(3));
1216                qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL");
1217                break;
1218
1219            case PHONES:
1220                qb.setTables(LegacyTables.PHONES);
1221                qb.setProjectionMap(sPhoneProjectionMap);
1222                applyRawContactsAccount(qb, uri);
1223                break;
1224
1225            case PHONES_ID:
1226                qb.setTables(LegacyTables.PHONES);
1227                qb.setProjectionMap(sPhoneProjectionMap);
1228                applyRawContactsAccount(qb, uri);
1229                qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "=");
1230                qb.appendWhere(uri.getPathSegments().get(1));
1231                break;
1232
1233            case PEOPLE_PHONES:
1234                qb.setTables(LegacyTables.PHONES);
1235                qb.setProjectionMap(sPhoneProjectionMap);
1236                applyRawContactsAccount(qb, uri);
1237                qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "=");
1238                qb.appendWhere(uri.getPathSegments().get(1));
1239                break;
1240
1241            case PEOPLE_PHONES_ID:
1242                qb.setTables(LegacyTables.PHONES);
1243                qb.setProjectionMap(sPhoneProjectionMap);
1244                applyRawContactsAccount(qb, uri);
1245                qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "=");
1246                qb.appendWhere(uri.getPathSegments().get(1));
1247                qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "=");
1248                qb.appendWhere(uri.getPathSegments().get(3));
1249                break;
1250
1251            case EXTENSIONS:
1252                qb.setTables(LegacyTables.EXTENSIONS);
1253                qb.setProjectionMap(sExtensionProjectionMap);
1254                applyRawContactsAccount(qb, uri);
1255                break;
1256
1257            case EXTENSIONS_ID:
1258                qb.setTables(LegacyTables.EXTENSIONS);
1259                qb.setProjectionMap(sExtensionProjectionMap);
1260                applyRawContactsAccount(qb, uri);
1261                qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "=");
1262                qb.appendWhere(uri.getPathSegments().get(1));
1263                break;
1264
1265            case PEOPLE_EXTENSIONS:
1266                qb.setTables(LegacyTables.EXTENSIONS);
1267                qb.setProjectionMap(sExtensionProjectionMap);
1268                applyRawContactsAccount(qb, uri);
1269                qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "=");
1270                qb.appendWhere(uri.getPathSegments().get(1));
1271                break;
1272
1273            case PEOPLE_EXTENSIONS_ID:
1274                qb.setTables(LegacyTables.EXTENSIONS);
1275                qb.setProjectionMap(sExtensionProjectionMap);
1276                applyRawContactsAccount(qb, uri);
1277                qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "=");
1278                qb.appendWhere(uri.getPathSegments().get(1));
1279                qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "=");
1280                qb.appendWhere(uri.getPathSegments().get(3));
1281                break;
1282
1283            case GROUPS:
1284                qb.setTables(LegacyTables.GROUPS);
1285                qb.setProjectionMap(sGroupProjectionMap);
1286                applyGroupAccount(qb, uri);
1287                break;
1288
1289            case GROUPS_ID:
1290                qb.setTables(LegacyTables.GROUPS);
1291                qb.setProjectionMap(sGroupProjectionMap);
1292                applyGroupAccount(qb, uri);
1293                qb.appendWhere(" AND " + android.provider.Contacts.Groups._ID + "=");
1294                qb.appendWhere(uri.getPathSegments().get(1));
1295                break;
1296
1297            case GROUPMEMBERSHIP:
1298                qb.setTables(LegacyTables.GROUP_MEMBERSHIP);
1299                qb.setProjectionMap(sGroupMembershipProjectionMap);
1300                applyRawContactsAccount(qb, uri);
1301                break;
1302
1303            case GROUPMEMBERSHIP_ID:
1304                qb.setTables(LegacyTables.GROUP_MEMBERSHIP);
1305                qb.setProjectionMap(sGroupMembershipProjectionMap);
1306                applyRawContactsAccount(qb, uri);
1307                qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "=");
1308                qb.appendWhere(uri.getPathSegments().get(1));
1309                break;
1310
1311            case PEOPLE_GROUPMEMBERSHIP:
1312                qb.setTables(LegacyTables.GROUP_MEMBERSHIP);
1313                qb.setProjectionMap(sGroupMembershipProjectionMap);
1314                applyRawContactsAccount(qb, uri);
1315                qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "=");
1316                qb.appendWhere(uri.getPathSegments().get(1));
1317                break;
1318
1319            case PEOPLE_GROUPMEMBERSHIP_ID:
1320                qb.setTables(LegacyTables.GROUP_MEMBERSHIP);
1321                qb.setProjectionMap(sGroupMembershipProjectionMap);
1322                applyRawContactsAccount(qb, uri);
1323                qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "=");
1324                qb.appendWhere(uri.getPathSegments().get(1));
1325                qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "=");
1326                qb.appendWhere(uri.getPathSegments().get(3));
1327                break;
1328
1329            case PEOPLE_PHOTO:
1330                qb.setTables(LegacyTables.PHOTOS);
1331                qb.setProjectionMap(sPhotoProjectionMap);
1332                applyRawContactsAccount(qb, uri);
1333                qb.appendWhere(" AND " + android.provider.Contacts.Photos.PERSON_ID + "=");
1334                qb.appendWhere(uri.getPathSegments().get(1));
1335                limit = "1";
1336                break;
1337
1338            case PRESENCE:
1339                qb.setTables(Tables.PRESENCE);
1340                qb.setProjectionMap(sPresenceProjectionMap);
1341                qb.appendWhere(mContactsProvider.getContactsRestrictionExceptionAsNestedQuery(
1342                        android.provider.Contacts.Presence.PERSON_ID));
1343                break;
1344
1345            case PRESENCE_ID:
1346                qb.setTables(Tables.PRESENCE);
1347                qb.setProjectionMap(sPresenceProjectionMap);
1348                qb.appendWhere(mContactsProvider.getContactsRestrictionExceptionAsNestedQuery(
1349                        android.provider.Contacts.Presence.PERSON_ID));
1350                qb.appendWhere(" AND " + android.provider.Contacts.Presence._ID + "=");
1351                qb.appendWhere(uri.getPathSegments().get(1));
1352                break;
1353
1354            case SEARCH_SUGGESTIONS:
1355
1356                // No legacy compatibility for search suggestions
1357                return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit);
1358
1359            case DELETED_PEOPLE:
1360            case DELETED_GROUPS:
1361                throw new UnsupportedOperationException();
1362
1363            default:
1364                throw new IllegalArgumentException("Unknown URL " + uri);
1365        }
1366
1367        // Perform the query and set the notification uri
1368        final Cursor c = qb.query(db, projection, selection, selectionArgs,
1369                groupBy, null, sortOrder, limit);
1370        if (c != null) {
1371            c.setNotificationUri(mContext.getContentResolver(), RawContacts.CONTENT_URI);
1372        }
1373        return c;
1374    }
1375
1376    private void applyRawContactsAccount(SQLiteQueryBuilder qb, Uri uri) {
1377        StringBuilder sb = new StringBuilder();
1378        sb.append(RawContacts.ACCOUNT_NAME + "=");
1379        DatabaseUtils.appendEscapedSQLString(sb, mAccount.mName);
1380        sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
1381        DatabaseUtils.appendEscapedSQLString(sb, mAccount.mType);
1382        qb.appendWhere(sb.toString());
1383    }
1384
1385    private void applyGroupAccount(SQLiteQueryBuilder qb, Uri uri) {
1386        StringBuilder sb = new StringBuilder();
1387        sb.append(Groups.ACCOUNT_NAME + "=");
1388        DatabaseUtils.appendEscapedSQLString(sb, mAccount.mName);
1389        sb.append(" AND " + Groups.ACCOUNT_TYPE + "=");
1390        DatabaseUtils.appendEscapedSQLString(sb, mAccount.mType);
1391        qb.appendWhere(sb.toString());
1392    }
1393
1394    /**
1395     * Called when a change has been made.
1396     *
1397     * @param uri the uri that the change was made to
1398     */
1399    private void onChange(Uri uri) {
1400        mContext.getContentResolver().notifyChange(android.provider.Contacts.CONTENT_URI, null);
1401    }
1402}
1403