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