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