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