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