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