• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package com.android.providers.contacts;
17 
18 import android.accounts.Account;
19 import android.app.SearchManager;
20 import android.content.ContentUris;
21 import android.content.ContentValues;
22 import android.content.Context;
23 import android.content.UriMatcher;
24 import android.database.Cursor;
25 import android.database.DatabaseUtils;
26 import android.database.SQLException;
27 import android.database.sqlite.SQLiteDatabase;
28 import android.database.sqlite.SQLiteDoneException;
29 import android.database.sqlite.SQLiteQueryBuilder;
30 import android.database.sqlite.SQLiteStatement;
31 import android.net.Uri;
32 import android.provider.BaseColumns;
33 import android.provider.Contacts.ContactMethods;
34 import android.provider.Contacts.Extensions;
35 import android.provider.Contacts.People;
36 import android.provider.ContactsContract;
37 import android.provider.ContactsContract.CommonDataKinds.Email;
38 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
39 import android.provider.ContactsContract.CommonDataKinds.Im;
40 import android.provider.ContactsContract.CommonDataKinds.Note;
41 import android.provider.ContactsContract.CommonDataKinds.Organization;
42 import android.provider.ContactsContract.CommonDataKinds.Phone;
43 import android.provider.ContactsContract.CommonDataKinds.Photo;
44 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
45 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
46 import android.provider.ContactsContract.Contacts;
47 import android.provider.ContactsContract.Data;
48 import android.provider.ContactsContract.Groups;
49 import android.provider.ContactsContract.RawContacts;
50 import android.provider.ContactsContract.Settings;
51 import android.provider.ContactsContract.StatusUpdates;
52 import android.text.TextUtils;
53 import android.util.Log;
54 
55 import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
56 import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
57 import com.android.providers.contacts.ContactsDatabaseHelper.ExtensionsColumns;
58 import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
59 import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
60 import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
61 import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
62 import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
63 import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
64 import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
65 import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
66 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
67 
68 import java.util.HashMap;
69 import java.util.Locale;
70 
71 @SuppressWarnings("deprecation")
72 public class LegacyApiSupport {
73 
74     private static final String TAG = "ContactsProviderV1";
75 
76     private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
77 
78     private static final int PEOPLE = 1;
79     private static final int PEOPLE_ID = 2;
80     private static final int PEOPLE_UPDATE_CONTACT_TIME = 3;
81     private static final int ORGANIZATIONS = 4;
82     private static final int ORGANIZATIONS_ID = 5;
83     private static final int PEOPLE_CONTACTMETHODS = 6;
84     private static final int PEOPLE_CONTACTMETHODS_ID = 7;
85     private static final int CONTACTMETHODS = 8;
86     private static final int CONTACTMETHODS_ID = 9;
87     private static final int PEOPLE_PHONES = 10;
88     private static final int PEOPLE_PHONES_ID = 11;
89     private static final int PHONES = 12;
90     private static final int PHONES_ID = 13;
91     private static final int EXTENSIONS = 14;
92     private static final int EXTENSIONS_ID = 15;
93     private static final int PEOPLE_EXTENSIONS = 16;
94     private static final int PEOPLE_EXTENSIONS_ID = 17;
95     private static final int GROUPS = 18;
96     private static final int GROUPS_ID = 19;
97     private static final int GROUPMEMBERSHIP = 20;
98     private static final int GROUPMEMBERSHIP_ID = 21;
99     private static final int PEOPLE_GROUPMEMBERSHIP = 22;
100     private static final int PEOPLE_GROUPMEMBERSHIP_ID = 23;
101     private static final int PEOPLE_PHOTO = 24;
102     private static final int PHOTOS = 25;
103     private static final int PHOTOS_ID = 26;
104     private static final int PEOPLE_FILTER = 29;
105     private static final int DELETED_PEOPLE = 30;
106     private static final int DELETED_GROUPS = 31;
107     private static final int SEARCH_SUGGESTIONS = 32;
108     private static final int SEARCH_SHORTCUT = 33;
109     private static final int PHONES_FILTER = 34;
110     private static final int LIVE_FOLDERS_PEOPLE = 35;
111     private static final int LIVE_FOLDERS_PEOPLE_GROUP_NAME = 36;
112     private static final int LIVE_FOLDERS_PEOPLE_WITH_PHONES = 37;
113     private static final int LIVE_FOLDERS_PEOPLE_FAVORITES = 38;
114     private static final int CONTACTMETHODS_EMAIL = 39;
115     private static final int GROUP_NAME_MEMBERS = 40;
116     private static final int GROUP_SYSTEM_ID_MEMBERS = 41;
117     private static final int PEOPLE_ORGANIZATIONS = 42;
118     private static final int PEOPLE_ORGANIZATIONS_ID = 43;
119     private static final int SETTINGS = 44;
120 
121     private static final String PEOPLE_JOINS =
122             " JOIN " + Tables.ACCOUNTS + " ON ("
123                 + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID + ")"
124             + " LEFT OUTER JOIN data name ON (raw_contacts._id = name.raw_contact_id"
125             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = name.mimetype_id)"
126                     + "='" + StructuredName.CONTENT_ITEM_TYPE + "')"
127             + " LEFT OUTER JOIN data organization ON (raw_contacts._id = organization.raw_contact_id"
128             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = organization.mimetype_id)"
129                     + "='" + Organization.CONTENT_ITEM_TYPE + "' AND organization.is_primary)"
130             + " LEFT OUTER JOIN data email ON (raw_contacts._id = email.raw_contact_id"
131             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = email.mimetype_id)"
132                     + "='" + Email.CONTENT_ITEM_TYPE + "' AND email.is_primary)"
133             + " LEFT OUTER JOIN data note ON (raw_contacts._id = note.raw_contact_id"
134             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = note.mimetype_id)"
135                     + "='" + Note.CONTENT_ITEM_TYPE + "')"
136             + " LEFT OUTER JOIN data phone ON (raw_contacts._id = phone.raw_contact_id"
137             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = phone.mimetype_id)"
138                     + "='" + Phone.CONTENT_ITEM_TYPE + "' AND phone.is_primary)";
139 
140     public static final String DATA_JOINS =
141             " JOIN mimetypes ON (mimetypes._id = data.mimetype_id)"
142             + " JOIN raw_contacts ON (raw_contacts._id = data.raw_contact_id)"
143             + PEOPLE_JOINS;
144 
145     public static final String PRESENCE_JOINS =
146             " LEFT OUTER JOIN " + Tables.PRESENCE +
147             " ON (" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID + "=" +
148                     "(SELECT MAX(" + StatusUpdates.DATA_ID + ")" +
149                     " FROM " + Tables.PRESENCE +
150                     " WHERE people._id = " + PresenceColumns.RAW_CONTACT_ID + ")" +
151             " )";
152 
153     private static final String PHONETIC_NAME_SQL = "trim(trim("
154             + "ifnull(name." + StructuredName.PHONETIC_GIVEN_NAME + ",' ')||' '||"
155             + "ifnull(name." + StructuredName.PHONETIC_MIDDLE_NAME + ",' '))||' '||"
156             + "ifnull(name." + StructuredName.PHONETIC_FAMILY_NAME + ",' ')) ";
157 
158     private static final String CONTACT_METHOD_KIND_SQL =
159             "CAST ((CASE WHEN mimetype='" + Email.CONTENT_ITEM_TYPE + "'"
160                 + " THEN " + android.provider.Contacts.KIND_EMAIL
161                 + " ELSE"
162                     + " (CASE WHEN mimetype='" + Im.CONTENT_ITEM_TYPE +"'"
163                         + " THEN " + android.provider.Contacts.KIND_IM
164                         + " ELSE"
165                         + " (CASE WHEN mimetype='" + StructuredPostal.CONTENT_ITEM_TYPE + "'"
166                             + " THEN "  + android.provider.Contacts.KIND_POSTAL
167                             + " ELSE"
168                                 + " NULL"
169                             + " END)"
170                         + " END)"
171                 + " END) AS INTEGER)";
172 
173     private static final String IM_PROTOCOL_SQL =
174             "(CASE WHEN " + StatusUpdates.PROTOCOL + "=" + Im.PROTOCOL_CUSTOM
175                 + " THEN 'custom:'||" + StatusUpdates.CUSTOM_PROTOCOL
176                 + " ELSE 'pre:'||" + StatusUpdates.PROTOCOL
177                 + " END)";
178 
179     private static String CONTACT_METHOD_DATA_SQL =
180             "(CASE WHEN " + Data.MIMETYPE + "='" + Im.CONTENT_ITEM_TYPE + "'"
181                 + " THEN (CASE WHEN " + Tables.DATA + "." + Im.PROTOCOL + "=" + Im.PROTOCOL_CUSTOM
182                     + " THEN 'custom:'||" + Tables.DATA + "." + Im.CUSTOM_PROTOCOL
183                     + " ELSE 'pre:'||" + Tables.DATA + "." + Im.PROTOCOL
184                     + " END)"
185                 + " ELSE " + Tables.DATA + "." + Email.DATA
186                 + " END)";
187 
188     private static final Uri LIVE_FOLDERS_CONTACTS_URI = Uri.withAppendedPath(
189             ContactsContract.AUTHORITY_URI, "live_folders/contacts");
190 
191     private static final Uri LIVE_FOLDERS_CONTACTS_WITH_PHONES_URI = Uri.withAppendedPath(
192             ContactsContract.AUTHORITY_URI, "live_folders/contacts_with_phones");
193 
194     private static final Uri LIVE_FOLDERS_CONTACTS_FAVORITES_URI = Uri.withAppendedPath(
195             ContactsContract.AUTHORITY_URI, "live_folders/favorites");
196 
197     private static final String CONTACTS_UPDATE_LASTTIMECONTACTED =
198             "UPDATE " + Tables.CONTACTS +
199             " SET " + Contacts.LAST_TIME_CONTACTED + "=? " +
200             "WHERE " + Contacts._ID + "=?";
201     private static final String RAWCONTACTS_UPDATE_LASTTIMECONTACTED =
202             "UPDATE " + Tables.RAW_CONTACTS + " SET "
203             + RawContacts.LAST_TIME_CONTACTED + "=? WHERE "
204             + RawContacts._ID + "=?";
205 
206     private String[] mSelectionArgs1 = new String[1];
207     private String[] mSelectionArgs2 = new String[2];
208 
209     public interface LegacyTables {
210         public static final String PEOPLE = "view_v1_people";
211         public static final String PEOPLE_JOIN_PRESENCE = "view_v1_people people " + PRESENCE_JOINS;
212         public static final String GROUPS = "view_v1_groups";
213         public static final String ORGANIZATIONS = "view_v1_organizations";
214         public static final String CONTACT_METHODS = "view_v1_contact_methods";
215         public static final String PHONES = "view_v1_phones";
216         public static final String EXTENSIONS = "view_v1_extensions";
217         public static final String GROUP_MEMBERSHIP = "view_v1_group_membership";
218         public static final String PHOTOS = "view_v1_photos";
219         public static final String SETTINGS = "v1_settings";
220     }
221 
222     private static final String[] ORGANIZATION_MIME_TYPES = new String[] {
223         Organization.CONTENT_ITEM_TYPE
224     };
225 
226     private static final String[] CONTACT_METHOD_MIME_TYPES = new String[] {
227         Email.CONTENT_ITEM_TYPE,
228         Im.CONTENT_ITEM_TYPE,
229         StructuredPostal.CONTENT_ITEM_TYPE,
230     };
231 
232     private static final String[] PHONE_MIME_TYPES = new String[] {
233         Phone.CONTENT_ITEM_TYPE
234     };
235 
236     private static final String[] PHOTO_MIME_TYPES = new String[] {
237         Photo.CONTENT_ITEM_TYPE
238     };
239 
240     private static final String[] GROUP_MEMBERSHIP_MIME_TYPES = new String[] {
241         GroupMembership.CONTENT_ITEM_TYPE
242     };
243 
244     private static final String[] EXTENSION_MIME_TYPES = new String[] {
245         android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE
246     };
247 
248     private interface IdQuery {
249         String[] COLUMNS = { BaseColumns._ID };
250 
251         int _ID = 0;
252     }
253 
254     /**
255      * A custom data row that is used to store legacy photo data fields no
256      * longer directly supported by the API.
257      */
258     private interface LegacyPhotoData {
259         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo_v1_extras";
260 
261         public static final String PHOTO_DATA_ID = Data.DATA1;
262         public static final String LOCAL_VERSION = Data.DATA2;
263         public static final String DOWNLOAD_REQUIRED = Data.DATA3;
264         public static final String EXISTS_ON_SERVER = Data.DATA4;
265         public static final String SYNC_ERROR = Data.DATA5;
266     }
267 
268     public static final String LEGACY_PHOTO_JOIN =
269             " LEFT OUTER JOIN data legacy_photo ON (raw_contacts._id = legacy_photo.raw_contact_id"
270             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = legacy_photo.mimetype_id)"
271                 + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'"
272             + " AND " + DataColumns.CONCRETE_ID + " = legacy_photo." + LegacyPhotoData.PHOTO_DATA_ID
273             + ")";
274 
275     private static final HashMap<String, String> sPeopleProjectionMap;
276     private static final HashMap<String, String> sOrganizationProjectionMap;
277     private static final HashMap<String, String> sContactMethodProjectionMap;
278     private static final HashMap<String, String> sPhoneProjectionMap;
279     private static final HashMap<String, String> sExtensionProjectionMap;
280     private static final HashMap<String, String> sGroupProjectionMap;
281     private static final HashMap<String, String> sGroupMembershipProjectionMap;
282     private static final HashMap<String, String> sPhotoProjectionMap;
283 
284     static {
285 
286         // Contacts URI matching table
287         UriMatcher matcher = sUriMatcher;
288 
289         String authority = android.provider.Contacts.AUTHORITY;
matcher.addURI(authority, "extensions", EXTENSIONS)290         matcher.addURI(authority, "extensions", EXTENSIONS);
matcher.addURI(authority, "extensions/#", EXTENSIONS_ID)291         matcher.addURI(authority, "extensions/#", EXTENSIONS_ID);
matcher.addURI(authority, "groups", GROUPS)292         matcher.addURI(authority, "groups", GROUPS);
matcher.addURI(authority, "groups/#", GROUPS_ID)293         matcher.addURI(authority, "groups/#", GROUPS_ID);
matcher.addURI(authority, "groups/name/*/members", GROUP_NAME_MEMBERS)294         matcher.addURI(authority, "groups/name/*/members", GROUP_NAME_MEMBERS);
295 //        matcher.addURI(authority, "groups/name/*/members/filter/*",
296 //                GROUP_NAME_MEMBERS_FILTER);
matcher.addURI(authority, "groups/system_id/*/members", GROUP_SYSTEM_ID_MEMBERS)297         matcher.addURI(authority, "groups/system_id/*/members", GROUP_SYSTEM_ID_MEMBERS);
298 //        matcher.addURI(authority, "groups/system_id/*/members/filter/*",
299 //                GROUP_SYSTEM_ID_MEMBERS_FILTER);
matcher.addURI(authority, "groupmembership", GROUPMEMBERSHIP)300         matcher.addURI(authority, "groupmembership", GROUPMEMBERSHIP);
matcher.addURI(authority, "groupmembership/#", GROUPMEMBERSHIP_ID)301         matcher.addURI(authority, "groupmembership/#", GROUPMEMBERSHIP_ID);
302 //        matcher.addURI(authority, "groupmembershipraw", GROUPMEMBERSHIP_RAW);
matcher.addURI(authority, "people", PEOPLE)303         matcher.addURI(authority, "people", PEOPLE);
304 //        matcher.addURI(authority, "people/strequent", PEOPLE_STREQUENT);
305 //        matcher.addURI(authority, "people/strequent/filter/*", PEOPLE_STREQUENT_FILTER);
matcher.addURI(authority, "people/filter/*", PEOPLE_FILTER)306         matcher.addURI(authority, "people/filter/*", PEOPLE_FILTER);
307 //        matcher.addURI(authority, "people/with_phones_filter/*",
308 //                PEOPLE_WITH_PHONES_FILTER);
309 //        matcher.addURI(authority, "people/with_email_or_im_filter/*",
310 //                PEOPLE_WITH_EMAIL_OR_IM_FILTER);
matcher.addURI(authority, "people/#", PEOPLE_ID)311         matcher.addURI(authority, "people/#", PEOPLE_ID);
matcher.addURI(authority, "people/#/extensions", PEOPLE_EXTENSIONS)312         matcher.addURI(authority, "people/#/extensions", PEOPLE_EXTENSIONS);
matcher.addURI(authority, "people/#/extensions/#", PEOPLE_EXTENSIONS_ID)313         matcher.addURI(authority, "people/#/extensions/#", PEOPLE_EXTENSIONS_ID);
matcher.addURI(authority, "people/#/phones", PEOPLE_PHONES)314         matcher.addURI(authority, "people/#/phones", PEOPLE_PHONES);
matcher.addURI(authority, "people/#/phones/#", PEOPLE_PHONES_ID)315         matcher.addURI(authority, "people/#/phones/#", PEOPLE_PHONES_ID);
316 //        matcher.addURI(authority, "people/#/phones_with_presence",
317 //                PEOPLE_PHONES_WITH_PRESENCE);
matcher.addURI(authority, "people/#/photo", PEOPLE_PHOTO)318         matcher.addURI(authority, "people/#/photo", PEOPLE_PHOTO);
319 //        matcher.addURI(authority, "people/#/photo/data", PEOPLE_PHOTO_DATA);
matcher.addURI(authority, "people/#/contact_methods", PEOPLE_CONTACTMETHODS)320         matcher.addURI(authority, "people/#/contact_methods", PEOPLE_CONTACTMETHODS);
321 //        matcher.addURI(authority, "people/#/contact_methods_with_presence",
322 //                PEOPLE_CONTACTMETHODS_WITH_PRESENCE);
matcher.addURI(authority, "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID)323         matcher.addURI(authority, "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
matcher.addURI(authority, "people/#/organizations", PEOPLE_ORGANIZATIONS)324         matcher.addURI(authority, "people/#/organizations", PEOPLE_ORGANIZATIONS);
matcher.addURI(authority, "people/#/organizations/#", PEOPLE_ORGANIZATIONS_ID)325         matcher.addURI(authority, "people/#/organizations/#", PEOPLE_ORGANIZATIONS_ID);
matcher.addURI(authority, "people/#/groupmembership", PEOPLE_GROUPMEMBERSHIP)326         matcher.addURI(authority, "people/#/groupmembership", PEOPLE_GROUPMEMBERSHIP);
matcher.addURI(authority, "people/#/groupmembership/#", PEOPLE_GROUPMEMBERSHIP_ID)327         matcher.addURI(authority, "people/#/groupmembership/#", PEOPLE_GROUPMEMBERSHIP_ID);
328 //        matcher.addURI(authority, "people/raw", PEOPLE_RAW);
329 //        matcher.addURI(authority, "people/owner", PEOPLE_OWNER);
matcher.addURI(authority, "people/#/update_contact_time", PEOPLE_UPDATE_CONTACT_TIME)330         matcher.addURI(authority, "people/#/update_contact_time",
331                 PEOPLE_UPDATE_CONTACT_TIME);
matcher.addURI(authority, "deleted_people", DELETED_PEOPLE)332         matcher.addURI(authority, "deleted_people", DELETED_PEOPLE);
matcher.addURI(authority, "deleted_groups", DELETED_GROUPS)333         matcher.addURI(authority, "deleted_groups", DELETED_GROUPS);
matcher.addURI(authority, "phones", PHONES)334         matcher.addURI(authority, "phones", PHONES);
335 //        matcher.addURI(authority, "phones_with_presence", PHONES_WITH_PRESENCE);
matcher.addURI(authority, "phones/filter/*", PHONES_FILTER)336         matcher.addURI(authority, "phones/filter/*", PHONES_FILTER);
337 //        matcher.addURI(authority, "phones/filter_name/*", PHONES_FILTER_NAME);
338 //        matcher.addURI(authority, "phones/mobile_filter_name/*",
339 //                PHONES_MOBILE_FILTER_NAME);
matcher.addURI(authority, "phones/#", PHONES_ID)340         matcher.addURI(authority, "phones/#", PHONES_ID);
matcher.addURI(authority, "photos", PHOTOS)341         matcher.addURI(authority, "photos", PHOTOS);
matcher.addURI(authority, "photos/#", PHOTOS_ID)342         matcher.addURI(authority, "photos/#", PHOTOS_ID);
matcher.addURI(authority, "contact_methods", CONTACTMETHODS)343         matcher.addURI(authority, "contact_methods", CONTACTMETHODS);
matcher.addURI(authority, "contact_methods/email", CONTACTMETHODS_EMAIL)344         matcher.addURI(authority, "contact_methods/email", CONTACTMETHODS_EMAIL);
345 //        matcher.addURI(authority, "contact_methods/email/*", CONTACTMETHODS_EMAIL_FILTER);
matcher.addURI(authority, "contact_methods/#", CONTACTMETHODS_ID)346         matcher.addURI(authority, "contact_methods/#", CONTACTMETHODS_ID);
347 //        matcher.addURI(authority, "contact_methods/with_presence",
348 //                CONTACTMETHODS_WITH_PRESENCE);
matcher.addURI(authority, "organizations", ORGANIZATIONS)349         matcher.addURI(authority, "organizations", ORGANIZATIONS);
matcher.addURI(authority, "organizations/#", ORGANIZATIONS_ID)350         matcher.addURI(authority, "organizations/#", ORGANIZATIONS_ID);
351 //        matcher.addURI(authority, "voice_dialer_timestamp", VOICE_DIALER_TIMESTAMP);
matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGESTIONS)352         matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY,
353                 SEARCH_SUGGESTIONS);
matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGESTIONS)354         matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
355                 SEARCH_SUGGESTIONS);
matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", SEARCH_SHORTCUT)356         matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
357                 SEARCH_SHORTCUT);
matcher.addURI(authority, "settings", SETTINGS)358         matcher.addURI(authority, "settings", SETTINGS);
359 
matcher.addURI(authority, "live_folders/people", LIVE_FOLDERS_PEOPLE)360         matcher.addURI(authority, "live_folders/people", LIVE_FOLDERS_PEOPLE);
matcher.addURI(authority, "live_folders/people/*", LIVE_FOLDERS_PEOPLE_GROUP_NAME)361         matcher.addURI(authority, "live_folders/people/*",
362                 LIVE_FOLDERS_PEOPLE_GROUP_NAME);
matcher.addURI(authority, "live_folders/people_with_phones", LIVE_FOLDERS_PEOPLE_WITH_PHONES)363         matcher.addURI(authority, "live_folders/people_with_phones",
364                 LIVE_FOLDERS_PEOPLE_WITH_PHONES);
matcher.addURI(authority, "live_folders/favorites", LIVE_FOLDERS_PEOPLE_FAVORITES)365         matcher.addURI(authority, "live_folders/favorites",
366                 LIVE_FOLDERS_PEOPLE_FAVORITES);
367 
368 
369         HashMap<String, String> peopleProjectionMap = new HashMap<String, String>();
peopleProjectionMap.put(People.NAME, People.NAME)370         peopleProjectionMap.put(People.NAME, People.NAME);
peopleProjectionMap.put(People.DISPLAY_NAME, People.DISPLAY_NAME)371         peopleProjectionMap.put(People.DISPLAY_NAME, People.DISPLAY_NAME);
peopleProjectionMap.put(People.PHONETIC_NAME, People.PHONETIC_NAME)372         peopleProjectionMap.put(People.PHONETIC_NAME, People.PHONETIC_NAME);
peopleProjectionMap.put(People.NOTES, People.NOTES)373         peopleProjectionMap.put(People.NOTES, People.NOTES);
peopleProjectionMap.put(People.TIMES_CONTACTED, People.TIMES_CONTACTED)374         peopleProjectionMap.put(People.TIMES_CONTACTED, People.TIMES_CONTACTED);
peopleProjectionMap.put(People.LAST_TIME_CONTACTED, People.LAST_TIME_CONTACTED)375         peopleProjectionMap.put(People.LAST_TIME_CONTACTED, People.LAST_TIME_CONTACTED);
peopleProjectionMap.put(People.CUSTOM_RINGTONE, People.CUSTOM_RINGTONE)376         peopleProjectionMap.put(People.CUSTOM_RINGTONE, People.CUSTOM_RINGTONE);
peopleProjectionMap.put(People.SEND_TO_VOICEMAIL, People.SEND_TO_VOICEMAIL)377         peopleProjectionMap.put(People.SEND_TO_VOICEMAIL, People.SEND_TO_VOICEMAIL);
peopleProjectionMap.put(People.STARRED, People.STARRED)378         peopleProjectionMap.put(People.STARRED, People.STARRED);
peopleProjectionMap.put(People.PRIMARY_ORGANIZATION_ID, People.PRIMARY_ORGANIZATION_ID)379         peopleProjectionMap.put(People.PRIMARY_ORGANIZATION_ID, People.PRIMARY_ORGANIZATION_ID);
peopleProjectionMap.put(People.PRIMARY_EMAIL_ID, People.PRIMARY_EMAIL_ID)380         peopleProjectionMap.put(People.PRIMARY_EMAIL_ID, People.PRIMARY_EMAIL_ID);
peopleProjectionMap.put(People.PRIMARY_PHONE_ID, People.PRIMARY_PHONE_ID)381         peopleProjectionMap.put(People.PRIMARY_PHONE_ID, People.PRIMARY_PHONE_ID);
382 
383         sPeopleProjectionMap = new HashMap<String, String>(peopleProjectionMap);
sPeopleProjectionMap.put(People._ID, People._ID)384         sPeopleProjectionMap.put(People._ID, People._ID);
sPeopleProjectionMap.put(People.NUMBER, People.NUMBER)385         sPeopleProjectionMap.put(People.NUMBER, People.NUMBER);
sPeopleProjectionMap.put(People.TYPE, People.TYPE)386         sPeopleProjectionMap.put(People.TYPE, People.TYPE);
sPeopleProjectionMap.put(People.LABEL, People.LABEL)387         sPeopleProjectionMap.put(People.LABEL, People.LABEL);
sPeopleProjectionMap.put(People.NUMBER_KEY, People.NUMBER_KEY)388         sPeopleProjectionMap.put(People.NUMBER_KEY, People.NUMBER_KEY);
sPeopleProjectionMap.put(People.IM_PROTOCOL, IM_PROTOCOL_SQL + " AS " + People.IM_PROTOCOL)389         sPeopleProjectionMap.put(People.IM_PROTOCOL, IM_PROTOCOL_SQL + " AS " + People.IM_PROTOCOL);
sPeopleProjectionMap.put(People.IM_HANDLE, People.IM_HANDLE)390         sPeopleProjectionMap.put(People.IM_HANDLE, People.IM_HANDLE);
sPeopleProjectionMap.put(People.IM_ACCOUNT, People.IM_ACCOUNT)391         sPeopleProjectionMap.put(People.IM_ACCOUNT, People.IM_ACCOUNT);
sPeopleProjectionMap.put(People.PRESENCE_STATUS, People.PRESENCE_STATUS)392         sPeopleProjectionMap.put(People.PRESENCE_STATUS, People.PRESENCE_STATUS);
sPeopleProjectionMap.put(People.PRESENCE_CUSTOM_STATUS, "(SELECT " + StatusUpdates.STATUS + " FROM " + Tables.STATUS_UPDATES + " JOIN " + Tables.DATA + "   ON(" + StatusUpdatesColumns.DATA_ID + "=" + DataColumns.CONCRETE_ID + ")" + " WHERE " + DataColumns.CONCRETE_RAW_CONTACT_ID + "=people." + People._ID + " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC " + " LIMIT 1" + ") AS " + People.PRESENCE_CUSTOM_STATUS)393         sPeopleProjectionMap.put(People.PRESENCE_CUSTOM_STATUS,
394                 "(SELECT " + StatusUpdates.STATUS +
395                 " FROM " + Tables.STATUS_UPDATES +
396                 " JOIN " + Tables.DATA +
397                 "   ON(" + StatusUpdatesColumns.DATA_ID + "=" + DataColumns.CONCRETE_ID + ")" +
398                 " WHERE " + DataColumns.CONCRETE_RAW_CONTACT_ID + "=people." + People._ID +
399                 " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC " +
400                 " LIMIT 1" +
401                 ") AS " + People.PRESENCE_CUSTOM_STATUS);
402 
403         sOrganizationProjectionMap = new HashMap<String, String>();
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations._ID, android.provider.Contacts.Organizations._ID)404         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations._ID,
405                 android.provider.Contacts.Organizations._ID);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.PERSON_ID, android.provider.Contacts.Organizations.PERSON_ID)406         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.PERSON_ID,
407                 android.provider.Contacts.Organizations.PERSON_ID);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.ISPRIMARY, android.provider.Contacts.Organizations.ISPRIMARY)408         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.ISPRIMARY,
409                 android.provider.Contacts.Organizations.ISPRIMARY);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.COMPANY, android.provider.Contacts.Organizations.COMPANY)410         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.COMPANY,
411                 android.provider.Contacts.Organizations.COMPANY);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TYPE, android.provider.Contacts.Organizations.TYPE)412         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TYPE,
413                 android.provider.Contacts.Organizations.TYPE);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.LABEL, android.provider.Contacts.Organizations.LABEL)414         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.LABEL,
415                 android.provider.Contacts.Organizations.LABEL);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TITLE, android.provider.Contacts.Organizations.TITLE)416         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TITLE,
417                 android.provider.Contacts.Organizations.TITLE);
418 
419         sContactMethodProjectionMap = new HashMap<String, String>(peopleProjectionMap);
sContactMethodProjectionMap.put(ContactMethods._ID, ContactMethods._ID)420         sContactMethodProjectionMap.put(ContactMethods._ID, ContactMethods._ID);
sContactMethodProjectionMap.put(ContactMethods.PERSON_ID, ContactMethods.PERSON_ID)421         sContactMethodProjectionMap.put(ContactMethods.PERSON_ID, ContactMethods.PERSON_ID);
sContactMethodProjectionMap.put(ContactMethods.KIND, ContactMethods.KIND)422         sContactMethodProjectionMap.put(ContactMethods.KIND, ContactMethods.KIND);
sContactMethodProjectionMap.put(ContactMethods.ISPRIMARY, ContactMethods.ISPRIMARY)423         sContactMethodProjectionMap.put(ContactMethods.ISPRIMARY, ContactMethods.ISPRIMARY);
sContactMethodProjectionMap.put(ContactMethods.TYPE, ContactMethods.TYPE)424         sContactMethodProjectionMap.put(ContactMethods.TYPE, ContactMethods.TYPE);
sContactMethodProjectionMap.put(ContactMethods.DATA, ContactMethods.DATA)425         sContactMethodProjectionMap.put(ContactMethods.DATA, ContactMethods.DATA);
sContactMethodProjectionMap.put(ContactMethods.LABEL, ContactMethods.LABEL)426         sContactMethodProjectionMap.put(ContactMethods.LABEL, ContactMethods.LABEL);
sContactMethodProjectionMap.put(ContactMethods.AUX_DATA, ContactMethods.AUX_DATA)427         sContactMethodProjectionMap.put(ContactMethods.AUX_DATA, ContactMethods.AUX_DATA);
428 
429         sPhoneProjectionMap = new HashMap<String, String>(peopleProjectionMap);
sPhoneProjectionMap.put(android.provider.Contacts.Phones._ID, android.provider.Contacts.Phones._ID)430         sPhoneProjectionMap.put(android.provider.Contacts.Phones._ID,
431                 android.provider.Contacts.Phones._ID);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.PERSON_ID, android.provider.Contacts.Phones.PERSON_ID)432         sPhoneProjectionMap.put(android.provider.Contacts.Phones.PERSON_ID,
433                 android.provider.Contacts.Phones.PERSON_ID);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.ISPRIMARY, android.provider.Contacts.Phones.ISPRIMARY)434         sPhoneProjectionMap.put(android.provider.Contacts.Phones.ISPRIMARY,
435                 android.provider.Contacts.Phones.ISPRIMARY);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER, android.provider.Contacts.Phones.NUMBER)436         sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER,
437                 android.provider.Contacts.Phones.NUMBER);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.TYPE, android.provider.Contacts.Phones.TYPE)438         sPhoneProjectionMap.put(android.provider.Contacts.Phones.TYPE,
439                 android.provider.Contacts.Phones.TYPE);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.LABEL, android.provider.Contacts.Phones.LABEL)440         sPhoneProjectionMap.put(android.provider.Contacts.Phones.LABEL,
441                 android.provider.Contacts.Phones.LABEL);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER_KEY, android.provider.Contacts.Phones.NUMBER_KEY)442         sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER_KEY,
443                 android.provider.Contacts.Phones.NUMBER_KEY);
444 
445         sExtensionProjectionMap = new HashMap<String, String>();
sExtensionProjectionMap.put(android.provider.Contacts.Extensions._ID, android.provider.Contacts.Extensions._ID)446         sExtensionProjectionMap.put(android.provider.Contacts.Extensions._ID,
447                 android.provider.Contacts.Extensions._ID);
sExtensionProjectionMap.put(android.provider.Contacts.Extensions.PERSON_ID, android.provider.Contacts.Extensions.PERSON_ID)448         sExtensionProjectionMap.put(android.provider.Contacts.Extensions.PERSON_ID,
449                 android.provider.Contacts.Extensions.PERSON_ID);
sExtensionProjectionMap.put(android.provider.Contacts.Extensions.NAME, android.provider.Contacts.Extensions.NAME)450         sExtensionProjectionMap.put(android.provider.Contacts.Extensions.NAME,
451                 android.provider.Contacts.Extensions.NAME);
sExtensionProjectionMap.put(android.provider.Contacts.Extensions.VALUE, android.provider.Contacts.Extensions.VALUE)452         sExtensionProjectionMap.put(android.provider.Contacts.Extensions.VALUE,
453                 android.provider.Contacts.Extensions.VALUE);
454 
455         sGroupProjectionMap = new HashMap<String, String>();
sGroupProjectionMap.put(android.provider.Contacts.Groups._ID, android.provider.Contacts.Groups._ID)456         sGroupProjectionMap.put(android.provider.Contacts.Groups._ID,
457                 android.provider.Contacts.Groups._ID);
sGroupProjectionMap.put(android.provider.Contacts.Groups.NAME, android.provider.Contacts.Groups.NAME)458         sGroupProjectionMap.put(android.provider.Contacts.Groups.NAME,
459                 android.provider.Contacts.Groups.NAME);
sGroupProjectionMap.put(android.provider.Contacts.Groups.NOTES, android.provider.Contacts.Groups.NOTES)460         sGroupProjectionMap.put(android.provider.Contacts.Groups.NOTES,
461                 android.provider.Contacts.Groups.NOTES);
sGroupProjectionMap.put(android.provider.Contacts.Groups.SYSTEM_ID, android.provider.Contacts.Groups.SYSTEM_ID)462         sGroupProjectionMap.put(android.provider.Contacts.Groups.SYSTEM_ID,
463                 android.provider.Contacts.Groups.SYSTEM_ID);
464 
465         sGroupMembershipProjectionMap = new HashMap<String, String>(sGroupProjectionMap);
sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership._ID, android.provider.Contacts.GroupMembership._ID)466         sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership._ID,
467                 android.provider.Contacts.GroupMembership._ID);
sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.PERSON_ID, android.provider.Contacts.GroupMembership.PERSON_ID)468         sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.PERSON_ID,
469                 android.provider.Contacts.GroupMembership.PERSON_ID);
sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.GROUP_ID, android.provider.Contacts.GroupMembership.GROUP_ID)470         sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.GROUP_ID,
471                 android.provider.Contacts.GroupMembership.GROUP_ID);
sGroupMembershipProjectionMap.put( android.provider.Contacts.GroupMembership.GROUP_SYNC_ID, android.provider.Contacts.GroupMembership.GROUP_SYNC_ID)472         sGroupMembershipProjectionMap.put(
473                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ID,
474                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ID);
sGroupMembershipProjectionMap.put( android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT, android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT)475         sGroupMembershipProjectionMap.put(
476                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT,
477                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT);
sGroupMembershipProjectionMap.put( android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE, android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE)478         sGroupMembershipProjectionMap.put(
479                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE,
480                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE);
481 
482         sPhotoProjectionMap = new HashMap<String, String>();
sPhotoProjectionMap.put(android.provider.Contacts.Photos._ID, android.provider.Contacts.Photos._ID)483         sPhotoProjectionMap.put(android.provider.Contacts.Photos._ID,
484                 android.provider.Contacts.Photos._ID);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.PERSON_ID, android.provider.Contacts.Photos.PERSON_ID)485         sPhotoProjectionMap.put(android.provider.Contacts.Photos.PERSON_ID,
486                 android.provider.Contacts.Photos.PERSON_ID);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.DATA, android.provider.Contacts.Photos.DATA)487         sPhotoProjectionMap.put(android.provider.Contacts.Photos.DATA,
488                 android.provider.Contacts.Photos.DATA);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.LOCAL_VERSION, android.provider.Contacts.Photos.LOCAL_VERSION)489         sPhotoProjectionMap.put(android.provider.Contacts.Photos.LOCAL_VERSION,
490                 android.provider.Contacts.Photos.LOCAL_VERSION);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.DOWNLOAD_REQUIRED, android.provider.Contacts.Photos.DOWNLOAD_REQUIRED)491         sPhotoProjectionMap.put(android.provider.Contacts.Photos.DOWNLOAD_REQUIRED,
492                 android.provider.Contacts.Photos.DOWNLOAD_REQUIRED);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.EXISTS_ON_SERVER, android.provider.Contacts.Photos.EXISTS_ON_SERVER)493         sPhotoProjectionMap.put(android.provider.Contacts.Photos.EXISTS_ON_SERVER,
494                 android.provider.Contacts.Photos.EXISTS_ON_SERVER);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.SYNC_ERROR, android.provider.Contacts.Photos.SYNC_ERROR)495         sPhotoProjectionMap.put(android.provider.Contacts.Photos.SYNC_ERROR,
496                 android.provider.Contacts.Photos.SYNC_ERROR);
497     }
498 
499     private final Context mContext;
500     private final ContactsDatabaseHelper mDbHelper;
501     private final ContactsProvider2 mContactsProvider;
502     private final NameSplitter mPhoneticNameSplitter;
503     private final GlobalSearchSupport mGlobalSearchSupport;
504 
505     private final SQLiteStatement mDataMimetypeQuery;
506     private final SQLiteStatement mDataRawContactIdQuery;
507 
508     private final ContentValues mValues = new ContentValues();
509     private final ContentValues mValues2 = new ContentValues();
510     private final ContentValues mValues3 = new ContentValues();
511     private boolean mDefaultAccountKnown;
512     private Account mAccount;
513 
514     private final long mMimetypeEmail;
515     private final long mMimetypeIm;
516     private final long mMimetypePostal;
517 
518 
LegacyApiSupport(Context context, ContactsDatabaseHelper contactsDatabaseHelper, ContactsProvider2 contactsProvider, GlobalSearchSupport globalSearchSupport)519     public LegacyApiSupport(Context context, ContactsDatabaseHelper contactsDatabaseHelper,
520             ContactsProvider2 contactsProvider, GlobalSearchSupport globalSearchSupport) {
521         mContext = context;
522         mContactsProvider = contactsProvider;
523         mDbHelper = contactsDatabaseHelper;
524         mGlobalSearchSupport = globalSearchSupport;
525 
526         mPhoneticNameSplitter = new NameSplitter("", "", "", context
527                 .getString(com.android.internal.R.string.common_name_conjunctions), Locale
528                 .getDefault());
529 
530         SQLiteDatabase db = mDbHelper.getReadableDatabase();
531         mDataMimetypeQuery = db.compileStatement(
532                 "SELECT " + DataColumns.MIMETYPE_ID +
533                 " FROM " + Tables.DATA +
534                 " WHERE " + Data._ID + "=?");
535 
536         mDataRawContactIdQuery = db.compileStatement(
537                 "SELECT " + Data.RAW_CONTACT_ID +
538                 " FROM " + Tables.DATA +
539                 " WHERE " + Data._ID + "=?");
540 
541         mMimetypeEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
542         mMimetypeIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
543         mMimetypePostal = mDbHelper.getMimeTypeId(StructuredPostal.CONTENT_ITEM_TYPE);
544     }
545 
ensureDefaultAccount()546     private void ensureDefaultAccount() {
547         if (!mDefaultAccountKnown) {
548             mAccount = mContactsProvider.getDefaultAccount();
549             mDefaultAccountKnown = true;
550         }
551     }
552 
createDatabase(SQLiteDatabase db)553     public static void createDatabase(SQLiteDatabase db) {
554         Log.i(TAG, "Bootstrapping database legacy support");
555         createViews(db);
556         createSettingsTable(db);
557     }
558 
createViews(SQLiteDatabase db)559     public static void createViews(SQLiteDatabase db) {
560 
561         String peopleColumns = "name." + StructuredName.DISPLAY_NAME
562                         + " AS " + People.NAME + ", " +
563                 Tables.RAW_CONTACTS + "." + RawContactsColumns.DISPLAY_NAME
564                         + " AS " + People.DISPLAY_NAME + ", " +
565                 PHONETIC_NAME_SQL
566                         + " AS " + People.PHONETIC_NAME + " , " +
567                 "note." + Note.NOTE
568                         + " AS " + People.NOTES + ", " +
569                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
570                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
571                 Tables.RAW_CONTACTS + "." + RawContacts.TIMES_CONTACTED
572                         + " AS " + People.TIMES_CONTACTED + ", " +
573                 Tables.RAW_CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED
574                         + " AS " + People.LAST_TIME_CONTACTED + ", " +
575                 Tables.RAW_CONTACTS + "." + RawContacts.CUSTOM_RINGTONE
576                         + " AS " + People.CUSTOM_RINGTONE + ", " +
577                 Tables.RAW_CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL
578                         + " AS " + People.SEND_TO_VOICEMAIL + ", " +
579                 Tables.RAW_CONTACTS + "." + RawContacts.STARRED
580                         + " AS " + People.STARRED + ", " +
581                 "organization." + Data._ID
582                         + " AS " + People.PRIMARY_ORGANIZATION_ID + ", " +
583                 "email." + Data._ID
584                         + " AS " + People.PRIMARY_EMAIL_ID + ", " +
585                 "phone." + Data._ID
586                         + " AS " + People.PRIMARY_PHONE_ID + ", " +
587                 "phone." + Phone.NUMBER
588                         + " AS " + People.NUMBER + ", " +
589                 "phone." + Phone.TYPE
590                         + " AS " + People.TYPE + ", " +
591                 "phone." + Phone.LABEL
592                         + " AS " + People.LABEL + ", " +
593                 "_PHONE_NUMBER_STRIPPED_REVERSED(phone." + Phone.NUMBER + ")"
594                         + " AS " + People.NUMBER_KEY;
595 
596         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PEOPLE + ";");
597         db.execSQL("CREATE VIEW " + LegacyTables.PEOPLE + " AS SELECT " +
598                 RawContactsColumns.CONCRETE_ID
599                         + " AS " + android.provider.Contacts.People._ID + ", " +
600                 peopleColumns +
601                 " FROM " + Tables.RAW_CONTACTS + PEOPLE_JOINS +
602                 " WHERE " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0;");
603 
604         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.ORGANIZATIONS + ";");
605         db.execSQL("CREATE VIEW " + LegacyTables.ORGANIZATIONS + " AS SELECT " +
606                 DataColumns.CONCRETE_ID
607                         + " AS " + android.provider.Contacts.Organizations._ID + ", " +
608                 Data.RAW_CONTACT_ID
609                         + " AS " + android.provider.Contacts.Organizations.PERSON_ID + ", " +
610                 Data.IS_PRIMARY
611                         + " AS " + android.provider.Contacts.Organizations.ISPRIMARY + ", " +
612                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
613                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
614                 Organization.COMPANY
615                         + " AS " + android.provider.Contacts.Organizations.COMPANY + ", " +
616                 Organization.TYPE
617                         + " AS " + android.provider.Contacts.Organizations.TYPE + ", " +
618                 Organization.LABEL
619                         + " AS " + android.provider.Contacts.Organizations.LABEL + ", " +
620                 Organization.TITLE
621                         + " AS " + android.provider.Contacts.Organizations.TITLE +
622                 " FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
623                 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
624                         + Organization.CONTENT_ITEM_TYPE + "'"
625                         + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
626         ";");
627 
628         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.CONTACT_METHODS + ";");
629         db.execSQL("CREATE VIEW " + LegacyTables.CONTACT_METHODS + " AS SELECT " +
630                 DataColumns.CONCRETE_ID
631                         + " AS " + ContactMethods._ID + ", " +
632                 DataColumns.CONCRETE_RAW_CONTACT_ID
633                         + " AS " + ContactMethods.PERSON_ID + ", " +
634                 CONTACT_METHOD_KIND_SQL
635                         + " AS " + ContactMethods.KIND + ", " +
636                 DataColumns.CONCRETE_IS_PRIMARY
637                         + " AS " + ContactMethods.ISPRIMARY + ", " +
638                 Tables.DATA + "." + Email.TYPE
639                         + " AS " + ContactMethods.TYPE + ", " +
640                 CONTACT_METHOD_DATA_SQL
641                         + " AS " + ContactMethods.DATA + ", " +
642                 Tables.DATA + "." + Email.LABEL
643                         + " AS " + ContactMethods.LABEL + ", " +
644                 DataColumns.CONCRETE_DATA14
645                         + " AS " + ContactMethods.AUX_DATA + ", " +
646                 peopleColumns +
647                 " FROM " + Tables.DATA + DATA_JOINS +
648                 " WHERE " + ContactMethods.KIND + " IS NOT NULL"
649                     + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
650         ";");
651 
652 
653         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHONES + ";");
654         db.execSQL("CREATE VIEW " + LegacyTables.PHONES + " AS SELECT DISTINCT " +
655                 DataColumns.CONCRETE_ID
656                         + " AS " + android.provider.Contacts.Phones._ID + ", " +
657                 DataColumns.CONCRETE_RAW_CONTACT_ID
658                         + " AS " + android.provider.Contacts.Phones.PERSON_ID + ", " +
659                 DataColumns.CONCRETE_IS_PRIMARY
660                         + " AS " + android.provider.Contacts.Phones.ISPRIMARY + ", " +
661                 Tables.DATA + "." + Phone.NUMBER
662                         + " AS " + android.provider.Contacts.Phones.NUMBER + ", " +
663                 Tables.DATA + "." + Phone.TYPE
664                         + " AS " + android.provider.Contacts.Phones.TYPE + ", " +
665                 Tables.DATA + "." + Phone.LABEL
666                         + " AS " + android.provider.Contacts.Phones.LABEL + ", " +
667                 "_PHONE_NUMBER_STRIPPED_REVERSED(" + Tables.DATA + "." + Phone.NUMBER + ")"
668                         + " AS " + android.provider.Contacts.Phones.NUMBER_KEY + ", " +
669                 peopleColumns +
670                 " FROM " + Tables.DATA
671                         + " JOIN " + Tables.PHONE_LOOKUP
672                         + " ON (" + Tables.DATA + "._id = "
673                                 + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID + ")"
674                         + DATA_JOINS +
675                 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
676                         + Phone.CONTENT_ITEM_TYPE + "'"
677                         + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
678         ";");
679 
680         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.EXTENSIONS + ";");
681         db.execSQL("CREATE VIEW " + LegacyTables.EXTENSIONS + " AS SELECT " +
682                 DataColumns.CONCRETE_ID
683                         + " AS " + android.provider.Contacts.Extensions._ID + ", " +
684                 DataColumns.CONCRETE_RAW_CONTACT_ID
685                         + " AS " + android.provider.Contacts.Extensions.PERSON_ID + ", " +
686                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
687                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
688                 ExtensionsColumns.NAME
689                         + " AS " + android.provider.Contacts.Extensions.NAME + ", " +
690                 ExtensionsColumns.VALUE
691                         + " AS " + android.provider.Contacts.Extensions.VALUE +
692                 " FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
693                 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
694                         + android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE + "'"
695                         + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
696         ";");
697 
698         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUPS + ";");
699         db.execSQL("CREATE VIEW " + LegacyTables.GROUPS + " AS SELECT " +
700                 GroupsColumns.CONCRETE_ID + " AS " + android.provider.Contacts.Groups._ID + ", " +
701                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
702                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
703                 Groups.TITLE + " AS " + android.provider.Contacts.Groups.NAME + ", " +
704                 Groups.NOTES + " AS " + android.provider.Contacts.Groups.NOTES + " , " +
705                 Groups.SYSTEM_ID + " AS " + android.provider.Contacts.Groups.SYSTEM_ID +
706                 " FROM " + Tables.GROUPS +
707                 " JOIN " + Tables.ACCOUNTS + " ON (" +
708                 GroupsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID + ")" +
709         ";");
710 
711         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUP_MEMBERSHIP + ";");
712         db.execSQL("CREATE VIEW " + LegacyTables.GROUP_MEMBERSHIP + " AS SELECT " +
713                 DataColumns.CONCRETE_ID
714                         + " AS " + android.provider.Contacts.GroupMembership._ID + ", " +
715                 DataColumns.CONCRETE_RAW_CONTACT_ID
716                         + " AS " + android.provider.Contacts.GroupMembership.PERSON_ID + ", " +
717                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
718                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
719                 GroupMembership.GROUP_ROW_ID
720                         + " AS " + android.provider.Contacts.GroupMembership.GROUP_ID + ", " +
721                 Groups.TITLE
722                         + " AS " + android.provider.Contacts.GroupMembership.NAME + ", " +
723                 Groups.NOTES
724                         + " AS " + android.provider.Contacts.GroupMembership.NOTES + ", " +
725                 Groups.SYSTEM_ID
726                         + " AS " + android.provider.Contacts.GroupMembership.SYSTEM_ID + ", " +
727                 GroupsColumns.CONCRETE_SOURCE_ID
728                         + " AS "
729                         + android.provider.Contacts.GroupMembership.GROUP_SYNC_ID + ", " +
730                 AccountsColumns.CONCRETE_ACCOUNT_NAME
731                         + " AS "
732                         + android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT + ", " +
733                 AccountsColumns.CONCRETE_ACCOUNT_TYPE
734                         + " AS "
735                         + android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE +
736                 " FROM " + Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS +
737                 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
738                         + GroupMembership.CONTENT_ITEM_TYPE + "'"
739                         + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
740         ";");
741 
742         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHOTOS + ";");
743         db.execSQL("CREATE VIEW " + LegacyTables.PHOTOS + " AS SELECT " +
744                 DataColumns.CONCRETE_ID
745                         + " AS " + android.provider.Contacts.Photos._ID + ", " +
746                 DataColumns.CONCRETE_RAW_CONTACT_ID
747                         + " AS " + android.provider.Contacts.Photos.PERSON_ID + ", " +
748                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
749                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
750                 Tables.DATA + "." + Photo.PHOTO
751                         + " AS " + android.provider.Contacts.Photos.DATA + ", " +
752                 "legacy_photo." + LegacyPhotoData.EXISTS_ON_SERVER
753                         + " AS " + android.provider.Contacts.Photos.EXISTS_ON_SERVER + ", " +
754                 "legacy_photo." + LegacyPhotoData.DOWNLOAD_REQUIRED
755                         + " AS " + android.provider.Contacts.Photos.DOWNLOAD_REQUIRED + ", " +
756                 "legacy_photo." + LegacyPhotoData.LOCAL_VERSION
757                         + " AS " + android.provider.Contacts.Photos.LOCAL_VERSION + ", " +
758                 "legacy_photo." + LegacyPhotoData.SYNC_ERROR
759                         + " AS " + android.provider.Contacts.Photos.SYNC_ERROR +
760                 " FROM " + Tables.DATA + DATA_JOINS + LEGACY_PHOTO_JOIN +
761                 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
762                         + Photo.CONTENT_ITEM_TYPE + "'"
763                         + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
764         ";");
765 
766     }
767 
createSettingsTable(SQLiteDatabase db)768     public static void createSettingsTable(SQLiteDatabase db) {
769         db.execSQL("DROP TABLE IF EXISTS " + LegacyTables.SETTINGS + ";");
770         db.execSQL("CREATE TABLE " + LegacyTables.SETTINGS + " (" +
771                 android.provider.Contacts.Settings._ID + " INTEGER PRIMARY KEY," +
772                 android.provider.Contacts.Settings._SYNC_ACCOUNT + " TEXT," +
773                 android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE + " TEXT," +
774                 android.provider.Contacts.Settings.KEY + " STRING NOT NULL," +
775                 android.provider.Contacts.Settings.VALUE + " STRING " +
776         ");");
777     }
778 
insert(Uri uri, ContentValues values)779     public Uri insert(Uri uri, ContentValues values) {
780         ensureDefaultAccount();
781         final int match = sUriMatcher.match(uri);
782         long id = 0;
783         switch (match) {
784             case PEOPLE:
785                 id = insertPeople(values);
786                 break;
787 
788             case ORGANIZATIONS:
789                 id = insertOrganization(values);
790                 break;
791 
792             case PEOPLE_CONTACTMETHODS: {
793                 long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
794                 id = insertContactMethod(rawContactId, values);
795                 break;
796             }
797 
798             case CONTACTMETHODS: {
799                 long rawContactId = getRequiredValue(values, ContactMethods.PERSON_ID);
800                 id = insertContactMethod(rawContactId, values);
801                 break;
802             }
803 
804             case PHONES: {
805                 long rawContactId = getRequiredValue(values,
806                         android.provider.Contacts.Phones.PERSON_ID);
807                 id = insertPhone(rawContactId, values);
808                 break;
809             }
810 
811             case PEOPLE_PHONES: {
812                 long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
813                 id = insertPhone(rawContactId, values);
814                 break;
815             }
816 
817             case EXTENSIONS: {
818                 long rawContactId = getRequiredValue(values,
819                         android.provider.Contacts.Extensions.PERSON_ID);
820                 id = insertExtension(rawContactId, values);
821                 break;
822             }
823 
824             case GROUPS:
825                 id = insertGroup(values);
826                 break;
827 
828             case GROUPMEMBERSHIP: {
829                 long rawContactId = getRequiredValue(values,
830                         android.provider.Contacts.GroupMembership.PERSON_ID);
831                 long groupId = getRequiredValue(values,
832                         android.provider.Contacts.GroupMembership.GROUP_ID);
833                 id = insertGroupMembership(rawContactId, groupId);
834                 break;
835             }
836 
837             default:
838                 throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
839         }
840 
841         if (id < 0) {
842             return null;
843         }
844 
845         final Uri result = ContentUris.withAppendedId(uri, id);
846         onChange(result);
847         return result;
848     }
849 
getRequiredValue(ContentValues values, String column)850     private long getRequiredValue(ContentValues values, String column) {
851         if (!values.containsKey(column)) {
852             throw new RuntimeException("Required value: " + column);
853         }
854 
855         return values.getAsLong(column);
856     }
857 
insertPeople(ContentValues values)858     private long insertPeople(ContentValues values) {
859         parsePeopleValues(values);
860 
861         Uri contactUri = mContactsProvider.insertInTransaction(RawContacts.CONTENT_URI, mValues);
862         long rawContactId = ContentUris.parseId(contactUri);
863 
864         if (mValues2.size() != 0) {
865             mValues2.put(Data.RAW_CONTACT_ID, rawContactId);
866             mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues2);
867         }
868         if (mValues3.size() != 0) {
869             mValues3.put(Data.RAW_CONTACT_ID, rawContactId);
870             mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues3);
871         }
872 
873         return rawContactId;
874     }
875 
insertOrganization(ContentValues values)876     private long insertOrganization(ContentValues values) {
877         parseOrganizationValues(values);
878         ContactsDatabaseHelper.copyLongValue(mValues, Data.RAW_CONTACT_ID,
879                 values, android.provider.Contacts.Organizations.PERSON_ID);
880 
881         Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
882 
883         return ContentUris.parseId(uri);
884     }
885 
insertPhone(long rawContactId, ContentValues values)886     private long insertPhone(long rawContactId, ContentValues values) {
887         parsePhoneValues(values);
888         mValues.put(Data.RAW_CONTACT_ID, rawContactId);
889 
890         Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
891 
892         return ContentUris.parseId(uri);
893     }
894 
insertContactMethod(long rawContactId, ContentValues values)895     private long insertContactMethod(long rawContactId, ContentValues values) {
896         Integer kind = values.getAsInteger(ContactMethods.KIND);
897         if (kind == null) {
898             throw new RuntimeException("Required value: " + ContactMethods.KIND);
899         }
900 
901         parseContactMethodValues(kind, values);
902 
903         mValues.put(Data.RAW_CONTACT_ID, rawContactId);
904         Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
905         return ContentUris.parseId(uri);
906     }
907 
insertExtension(long rawContactId, ContentValues values)908     private long insertExtension(long rawContactId, ContentValues values) {
909         mValues.clear();
910 
911         mValues.put(Data.RAW_CONTACT_ID, rawContactId);
912         mValues.put(Data.MIMETYPE, android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE);
913 
914         parseExtensionValues(values);
915 
916         Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
917         return ContentUris.parseId(uri);
918     }
919 
insertGroup(ContentValues values)920     private long insertGroup(ContentValues values) {
921         parseGroupValues(values);
922 
923         if (mAccount != null) {
924             mValues.put(Groups.ACCOUNT_NAME, mAccount.name);
925             mValues.put(Groups.ACCOUNT_TYPE, mAccount.type);
926         }
927 
928         Uri uri = mContactsProvider.insertInTransaction(Groups.CONTENT_URI, mValues);
929         return ContentUris.parseId(uri);
930     }
931 
insertGroupMembership(long rawContactId, long groupId)932     private long insertGroupMembership(long rawContactId, long groupId) {
933         mValues.clear();
934 
935         mValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
936         mValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
937         mValues.put(GroupMembership.GROUP_ROW_ID, groupId);
938 
939         Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
940         return ContentUris.parseId(uri);
941     }
942 
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)943     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
944         ensureDefaultAccount();
945 
946         int match = sUriMatcher.match(uri);
947         int count = 0;
948         switch(match) {
949             case PEOPLE_UPDATE_CONTACT_TIME: {
950                 count = updateContactTime(uri, values);
951                 break;
952             }
953 
954             case PEOPLE_PHOTO: {
955                 long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
956                 return updatePhoto(rawContactId, values);
957             }
958 
959             case SETTINGS: {
960                 return updateSettings(values);
961             }
962 
963             case GROUPMEMBERSHIP:
964             case GROUPMEMBERSHIP_ID:
965             case -1: {
966                 throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
967             }
968 
969             default: {
970                 count = updateAll(uri, match, values, selection, selectionArgs);
971             }
972         }
973 
974         if (count > 0) {
975             mContext.getContentResolver().notifyChange(uri, null);
976         }
977 
978         return count;
979     }
980 
updateAll(Uri uri, final int match, ContentValues values, String selection, String[] selectionArgs)981     private int updateAll(Uri uri, final int match, ContentValues values, String selection,
982             String[] selectionArgs) {
983         Cursor c = query(uri, IdQuery.COLUMNS, selection, selectionArgs, null, null);
984         if (c == null) {
985             return 0;
986         }
987 
988         int count = 0;
989         try {
990             while (c.moveToNext()) {
991                 long id = c.getLong(IdQuery._ID);
992                 count += update(match, id, values);
993             }
994         } finally {
995             c.close();
996         }
997 
998         return count;
999     }
1000 
update(int match, long id, ContentValues values)1001     public int update(int match, long id, ContentValues values) {
1002         int count = 0;
1003         switch(match) {
1004             case PEOPLE:
1005             case PEOPLE_ID: {
1006                 count = updatePeople(id, values);
1007                 break;
1008             }
1009 
1010             case ORGANIZATIONS:
1011             case ORGANIZATIONS_ID: {
1012                 count = updateOrganizations(id, values);
1013                 break;
1014             }
1015 
1016             case PHONES:
1017             case PHONES_ID: {
1018                 count = updatePhones(id, values);
1019                 break;
1020             }
1021 
1022             case CONTACTMETHODS:
1023             case CONTACTMETHODS_ID: {
1024                 count = updateContactMethods(id, values);
1025                 break;
1026             }
1027 
1028             case EXTENSIONS:
1029             case EXTENSIONS_ID: {
1030                 count = updateExtensions(id, values);
1031                 break;
1032             }
1033 
1034             case GROUPS:
1035             case GROUPS_ID: {
1036                 count = updateGroups(id, values);
1037                 break;
1038             }
1039 
1040             case PHOTOS:
1041             case PHOTOS_ID:
1042                 count = updatePhotoByDataId(id, values);
1043                 break;
1044         }
1045 
1046         return count;
1047     }
1048 
updatePeople(long rawContactId, ContentValues values)1049     private int updatePeople(long rawContactId, ContentValues values) {
1050         parsePeopleValues(values);
1051 
1052         int count = mContactsProvider.updateInTransaction(RawContacts.CONTENT_URI,
1053                 mValues, RawContacts._ID + "=" + rawContactId, null);
1054 
1055         if (count == 0) {
1056             return 0;
1057         }
1058 
1059         if (mValues2.size() != 0) {
1060             Uri dataUri = findFirstDataRow(rawContactId, StructuredName.CONTENT_ITEM_TYPE);
1061             if (dataUri != null) {
1062                 mContactsProvider.updateInTransaction(dataUri, mValues2, null, null);
1063             } else {
1064                 mValues2.put(Data.RAW_CONTACT_ID, rawContactId);
1065                 mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues2);
1066             }
1067         }
1068 
1069         if (mValues3.size() != 0) {
1070             Uri dataUri = findFirstDataRow(rawContactId, Note.CONTENT_ITEM_TYPE);
1071             if (dataUri != null) {
1072                 mContactsProvider.updateInTransaction(dataUri, mValues3, null, null);
1073             } else {
1074                 mValues3.put(Data.RAW_CONTACT_ID, rawContactId);
1075                 mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues3);
1076             }
1077         }
1078 
1079         if (values.containsKey(People.LAST_TIME_CONTACTED) &&
1080                 !values.containsKey(People.TIMES_CONTACTED)) {
1081             updateContactTime(rawContactId, values);
1082         }
1083 
1084         return count;
1085     }
1086 
updateOrganizations(long dataId, ContentValues values)1087     private int updateOrganizations(long dataId, ContentValues values) {
1088         parseOrganizationValues(values);
1089 
1090         return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1091                 Data._ID + "=" + dataId, null);
1092     }
1093 
updatePhones(long dataId, ContentValues values)1094     private int updatePhones(long dataId, ContentValues values) {
1095         parsePhoneValues(values);
1096 
1097         return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1098                 Data._ID + "=" + dataId, null);
1099     }
1100 
updateContactMethods(long dataId, ContentValues values)1101     private int updateContactMethods(long dataId, ContentValues values) {
1102         int kind;
1103 
1104         mDataMimetypeQuery.bindLong(1, dataId);
1105         long mimetype_id;
1106         try {
1107             mimetype_id = mDataMimetypeQuery.simpleQueryForLong();
1108         } catch (SQLiteDoneException e) {
1109             // Data row not found
1110             return 0;
1111         }
1112 
1113         if (mimetype_id == mMimetypeEmail) {
1114             kind = android.provider.Contacts.KIND_EMAIL;
1115         } else if (mimetype_id == mMimetypeIm) {
1116             kind = android.provider.Contacts.KIND_IM;
1117         } else if (mimetype_id == mMimetypePostal) {
1118             kind = android.provider.Contacts.KIND_POSTAL;
1119         } else {
1120 
1121             // Non-legacy kind: return "Not found"
1122             return 0;
1123         }
1124 
1125         parseContactMethodValues(kind, values);
1126 
1127         return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1128                 Data._ID + "=" + dataId, null);
1129     }
1130 
updateExtensions(long dataId, ContentValues values)1131     private int updateExtensions(long dataId, ContentValues values) {
1132         parseExtensionValues(values);
1133 
1134         return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1135                 Data._ID + "=" + dataId, null);
1136     }
1137 
updateGroups(long groupId, ContentValues values)1138     private int updateGroups(long groupId, ContentValues values) {
1139         parseGroupValues(values);
1140 
1141         return mContactsProvider.updateInTransaction(Groups.CONTENT_URI, mValues,
1142                 Groups._ID + "=" + groupId, null);
1143     }
1144 
updateContactTime(Uri uri, ContentValues values)1145     private int updateContactTime(Uri uri, ContentValues values) {
1146         long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
1147         updateContactTime(rawContactId, values);
1148         return 1;
1149     }
1150 
updateContactTime(long rawContactId, ContentValues values)1151     private void updateContactTime(long rawContactId, ContentValues values) {
1152         long lastTimeContacted;
1153         if (values.containsKey(People.LAST_TIME_CONTACTED)) {
1154             lastTimeContacted = values.getAsLong(People.LAST_TIME_CONTACTED);
1155         } else {
1156             lastTimeContacted = System.currentTimeMillis();
1157         }
1158 
1159         // TODO check sanctions
1160         long contactId = mDbHelper.getContactId(rawContactId);
1161         SQLiteDatabase mDb = mDbHelper.getWritableDatabase();
1162         mSelectionArgs2[0] = String.valueOf(lastTimeContacted);
1163         if (contactId != 0) {
1164             mSelectionArgs2[1] = String.valueOf(contactId);
1165             mDb.execSQL(CONTACTS_UPDATE_LASTTIMECONTACTED, mSelectionArgs2);
1166             // increment times_contacted column
1167             mSelectionArgs1[0] = String.valueOf(contactId);
1168             mDb.execSQL(ContactsProvider2.UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
1169         }
1170         mSelectionArgs2[1] = String.valueOf(rawContactId);
1171         mDb.execSQL(RAWCONTACTS_UPDATE_LASTTIMECONTACTED, mSelectionArgs2);
1172         // increment times_contacted column
1173         mSelectionArgs1[0] = String.valueOf(contactId);
1174         mDb.execSQL(ContactsProvider2.UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
1175     }
1176 
updatePhoto(long rawContactId, ContentValues values)1177     private int updatePhoto(long rawContactId, ContentValues values) {
1178 
1179         // TODO check sanctions
1180 
1181         int count;
1182 
1183         long dataId = findFirstDataId(rawContactId, Photo.CONTENT_ITEM_TYPE);
1184 
1185         mValues.clear();
1186         byte[] bytes = values.getAsByteArray(android.provider.Contacts.Photos.DATA);
1187         mValues.put(Photo.PHOTO, bytes);
1188 
1189         if (dataId == -1) {
1190             mValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
1191             mValues.put(Data.RAW_CONTACT_ID, rawContactId);
1192             Uri dataUri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
1193             dataId = ContentUris.parseId(dataUri);
1194             count = 1;
1195         } else {
1196             Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
1197             count = mContactsProvider.updateInTransaction(dataUri, mValues, null, null);
1198         }
1199 
1200         updateLegacyPhotoData(rawContactId, dataId, values);
1201 
1202         return count;
1203     }
1204 
updatePhotoByDataId(long dataId, ContentValues values)1205     private int updatePhotoByDataId(long dataId, ContentValues values) {
1206 
1207         mDataRawContactIdQuery.bindLong(1, dataId);
1208         long rawContactId;
1209 
1210         try {
1211             rawContactId = mDataRawContactIdQuery.simpleQueryForLong();
1212         } catch (SQLiteDoneException e) {
1213             // Data row not found
1214             return 0;
1215         }
1216 
1217         if (values.containsKey(android.provider.Contacts.Photos.DATA)) {
1218             byte[] bytes = values.getAsByteArray(android.provider.Contacts.Photos.DATA);
1219             mValues.clear();
1220             mValues.put(Photo.PHOTO, bytes);
1221             mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1222                     Data._ID + "=" + dataId, null);
1223         }
1224 
1225         updateLegacyPhotoData(rawContactId, dataId, values);
1226 
1227         return 1;
1228     }
1229 
updateLegacyPhotoData(long rawContactId, long dataId, ContentValues values)1230     private void updateLegacyPhotoData(long rawContactId, long dataId, ContentValues values) {
1231         mValues.clear();
1232         ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.LOCAL_VERSION,
1233                 values, android.provider.Contacts.Photos.LOCAL_VERSION);
1234         ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.DOWNLOAD_REQUIRED,
1235                 values, android.provider.Contacts.Photos.DOWNLOAD_REQUIRED);
1236         ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.EXISTS_ON_SERVER,
1237                 values, android.provider.Contacts.Photos.EXISTS_ON_SERVER);
1238         ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.SYNC_ERROR,
1239                 values, android.provider.Contacts.Photos.SYNC_ERROR);
1240 
1241         int updated = mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
1242                 Data.MIMETYPE + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'"
1243                         + " AND " + Data.RAW_CONTACT_ID + "=" + rawContactId
1244                         + " AND " + LegacyPhotoData.PHOTO_DATA_ID + "=" + dataId, null);
1245         if (updated == 0) {
1246             mValues.put(Data.RAW_CONTACT_ID, rawContactId);
1247             mValues.put(Data.MIMETYPE, LegacyPhotoData.CONTENT_ITEM_TYPE);
1248             mValues.put(LegacyPhotoData.PHOTO_DATA_ID, dataId);
1249             mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
1250         }
1251     }
1252 
updateSettings(ContentValues values)1253     private int updateSettings(ContentValues values) {
1254         SQLiteDatabase db = mDbHelper.getWritableDatabase();
1255         String accountName = values.getAsString(android.provider.Contacts.Settings._SYNC_ACCOUNT);
1256         String accountType =
1257                 values.getAsString(android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE);
1258         String key = values.getAsString(android.provider.Contacts.Settings.KEY);
1259         if (key == null) {
1260             throw new IllegalArgumentException("you must specify the key when updating settings");
1261         }
1262         updateSetting(db, accountName, accountType, values);
1263         if (key.equals(android.provider.Contacts.Settings.SYNC_EVERYTHING)) {
1264             mValues.clear();
1265             mValues.put(Settings.SHOULD_SYNC,
1266                     values.getAsInteger(android.provider.Contacts.Settings.VALUE));
1267             String selection;
1268             String[] selectionArgs;
1269             if (accountName != null && accountType != null) {
1270 
1271                 selectionArgs = new String[]{accountName, accountType};
1272                 selection = Settings.ACCOUNT_NAME + "=?"
1273                         + " AND " + Settings.ACCOUNT_TYPE + "=?"
1274                         + " AND " + Settings.DATA_SET + " IS NULL";
1275             } else {
1276                 selectionArgs = null;
1277                 selection = Settings.ACCOUNT_NAME + " IS NULL"
1278                         + " AND " + Settings.ACCOUNT_TYPE + " IS NULL"
1279                         + " AND " + Settings.DATA_SET + " IS NULL";
1280             }
1281             int count = mContactsProvider.updateInTransaction(Settings.CONTENT_URI, mValues,
1282                     selection, selectionArgs);
1283             if (count == 0) {
1284                 mValues.put(Settings.ACCOUNT_NAME, accountName);
1285                 mValues.put(Settings.ACCOUNT_TYPE, accountType);
1286                 mContactsProvider.insertInTransaction(Settings.CONTENT_URI, mValues);
1287             }
1288         }
1289         return 1;
1290     }
1291 
updateSetting(SQLiteDatabase db, String accountName, String accountType, ContentValues values)1292     private void updateSetting(SQLiteDatabase db, String accountName, String accountType,
1293             ContentValues values) {
1294         final String key = values.getAsString(android.provider.Contacts.Settings.KEY);
1295         if (accountName == null || accountType == null) {
1296             db.delete(LegacyTables.SETTINGS, "_sync_account IS NULL AND key=?", new String[]{key});
1297         } else {
1298             db.delete(LegacyTables.SETTINGS, "_sync_account=? AND _sync_account_type=? AND key=?",
1299                     new String[]{accountName, accountType, key});
1300         }
1301         long rowId = db.insert(LegacyTables.SETTINGS,
1302                 android.provider.Contacts.Settings.KEY, values);
1303         if (rowId < 0) {
1304             throw new SQLException("error updating settings with " + values);
1305         }
1306     }
1307 
1308     private interface SettingsMatchQuery {
1309         String SQL =
1310             "SELECT "
1311                     + ContactsContract.Settings.ACCOUNT_NAME + ","
1312                     + ContactsContract.Settings.ACCOUNT_TYPE + ","
1313                     + ContactsContract.Settings.SHOULD_SYNC +
1314             " FROM " + Tables.SETTINGS + " LEFT OUTER JOIN " + LegacyTables.SETTINGS +
1315             " ON (" + ContactsContract.Settings.ACCOUNT_NAME + "="
1316                               + android.provider.Contacts.Settings._SYNC_ACCOUNT +
1317                       " AND " + ContactsContract.Settings.ACCOUNT_TYPE + "="
1318                               + android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE +
1319                       " AND " + ContactsContract.Settings.DATA_SET + " IS NULL" +
1320                       " AND " + android.provider.Contacts.Settings.KEY + "='"
1321                               + android.provider.Contacts.Settings.SYNC_EVERYTHING + "'" +
1322             ")" +
1323             " WHERE " + ContactsContract.Settings.SHOULD_SYNC + "<>"
1324                             + android.provider.Contacts.Settings.VALUE;
1325 
1326         int ACCOUNT_NAME = 0;
1327         int ACCOUNT_TYPE = 1;
1328         int SHOULD_SYNC = 2;
1329     }
1330 
1331     /**
1332      * Brings legacy settings table in sync with the new settings.
1333      */
copySettingsToLegacySettings()1334     public void copySettingsToLegacySettings() {
1335         SQLiteDatabase db = mDbHelper.getWritableDatabase();
1336         Cursor cursor = db.rawQuery(SettingsMatchQuery.SQL, null);
1337         try {
1338             while(cursor.moveToNext()) {
1339                 String accountName = cursor.getString(SettingsMatchQuery.ACCOUNT_NAME);
1340                 String accountType = cursor.getString(SettingsMatchQuery.ACCOUNT_TYPE);
1341                 String value = cursor.getString(SettingsMatchQuery.SHOULD_SYNC);
1342                 mValues.clear();
1343                 mValues.put(android.provider.Contacts.Settings._SYNC_ACCOUNT, accountName);
1344                 mValues.put(android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE, accountType);
1345                 mValues.put(android.provider.Contacts.Settings.KEY,
1346                         android.provider.Contacts.Settings.SYNC_EVERYTHING);
1347                 mValues.put(android.provider.Contacts.Settings.VALUE, value);
1348                 updateSetting(db, accountName, accountType, mValues);
1349             }
1350         } finally {
1351             cursor.close();
1352         }
1353     }
1354 
parsePeopleValues(ContentValues values)1355     private void parsePeopleValues(ContentValues values) {
1356         mValues.clear();
1357         mValues2.clear();
1358         mValues3.clear();
1359 
1360         ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
1361                 values, People.CUSTOM_RINGTONE);
1362         ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
1363                 values, People.SEND_TO_VOICEMAIL);
1364         ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
1365                 values, People.LAST_TIME_CONTACTED);
1366         ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
1367                 values, People.TIMES_CONTACTED);
1368         ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
1369                 values, People.STARRED);
1370         if (mAccount != null) {
1371             mValues.put(RawContacts.ACCOUNT_NAME, mAccount.name);
1372             mValues.put(RawContacts.ACCOUNT_TYPE, mAccount.type);
1373         }
1374 
1375         if (values.containsKey(People.NAME) || values.containsKey(People.PHONETIC_NAME)) {
1376             mValues2.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
1377             ContactsDatabaseHelper.copyStringValue(mValues2, StructuredName.DISPLAY_NAME,
1378                     values, People.NAME);
1379             if (values.containsKey(People.PHONETIC_NAME)) {
1380                 String phoneticName = values.getAsString(People.PHONETIC_NAME);
1381                 NameSplitter.Name parsedName = new NameSplitter.Name();
1382                 mPhoneticNameSplitter.split(parsedName, phoneticName);
1383                 mValues2.put(StructuredName.PHONETIC_GIVEN_NAME, parsedName.getGivenNames());
1384                 mValues2.put(StructuredName.PHONETIC_MIDDLE_NAME, parsedName.getMiddleName());
1385                 mValues2.put(StructuredName.PHONETIC_FAMILY_NAME, parsedName.getFamilyName());
1386             }
1387         }
1388 
1389         if (values.containsKey(People.NOTES)) {
1390             mValues3.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
1391             ContactsDatabaseHelper.copyStringValue(mValues3, Note.NOTE, values, People.NOTES);
1392         }
1393     }
1394 
parseOrganizationValues(ContentValues values)1395     private void parseOrganizationValues(ContentValues values) {
1396         mValues.clear();
1397 
1398         mValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
1399 
1400         ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY,
1401                 values, android.provider.Contacts.Organizations.ISPRIMARY);
1402 
1403         ContactsDatabaseHelper.copyStringValue(mValues, Organization.COMPANY,
1404                 values, android.provider.Contacts.Organizations.COMPANY);
1405 
1406         // TYPE values happen to remain the same between V1 and V2 - can just copy the value
1407         ContactsDatabaseHelper.copyLongValue(mValues, Organization.TYPE,
1408                 values, android.provider.Contacts.Organizations.TYPE);
1409 
1410         ContactsDatabaseHelper.copyStringValue(mValues, Organization.LABEL,
1411                 values, android.provider.Contacts.Organizations.LABEL);
1412         ContactsDatabaseHelper.copyStringValue(mValues, Organization.TITLE,
1413                 values, android.provider.Contacts.Organizations.TITLE);
1414     }
1415 
parsePhoneValues(ContentValues values)1416     private void parsePhoneValues(ContentValues values) {
1417         mValues.clear();
1418 
1419         mValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1420 
1421         ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY,
1422                 values, android.provider.Contacts.Phones.ISPRIMARY);
1423 
1424         ContactsDatabaseHelper.copyStringValue(mValues, Phone.NUMBER,
1425                 values, android.provider.Contacts.Phones.NUMBER);
1426 
1427         // TYPE values happen to remain the same between V1 and V2 - can just copy the value
1428         ContactsDatabaseHelper.copyLongValue(mValues, Phone.TYPE,
1429                 values, android.provider.Contacts.Phones.TYPE);
1430 
1431         ContactsDatabaseHelper.copyStringValue(mValues, Phone.LABEL,
1432                 values, android.provider.Contacts.Phones.LABEL);
1433     }
1434 
parseContactMethodValues(int kind, ContentValues values)1435     private void parseContactMethodValues(int kind, ContentValues values) {
1436         mValues.clear();
1437 
1438         ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY, values,
1439                 ContactMethods.ISPRIMARY);
1440 
1441         switch (kind) {
1442             case android.provider.Contacts.KIND_EMAIL: {
1443                 copyCommonFields(values, Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL,
1444                         Data.DATA14);
1445                 ContactsDatabaseHelper.copyStringValue(mValues, Email.DATA, values,
1446                         ContactMethods.DATA);
1447                 break;
1448             }
1449 
1450             case android.provider.Contacts.KIND_IM: {
1451                 String protocol = values.getAsString(ContactMethods.DATA);
1452                 if (protocol.startsWith("pre:")) {
1453                     mValues.put(Im.PROTOCOL, Integer.parseInt(protocol.substring(4)));
1454                 } else if (protocol.startsWith("custom:")) {
1455                     mValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
1456                     mValues.put(Im.CUSTOM_PROTOCOL, protocol.substring(7));
1457                 }
1458 
1459                 copyCommonFields(values, Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL, Data.DATA14);
1460                 break;
1461             }
1462 
1463             case android.provider.Contacts.KIND_POSTAL: {
1464                 copyCommonFields(values, StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE,
1465                         StructuredPostal.LABEL, Data.DATA14);
1466                 ContactsDatabaseHelper.copyStringValue(mValues, StructuredPostal.FORMATTED_ADDRESS,
1467                         values, ContactMethods.DATA);
1468                 break;
1469             }
1470         }
1471     }
1472 
copyCommonFields(ContentValues values, String mimeType, String typeColumn, String labelColumn, String auxDataColumn)1473     private void copyCommonFields(ContentValues values, String mimeType, String typeColumn,
1474             String labelColumn, String auxDataColumn) {
1475         mValues.put(Data.MIMETYPE, mimeType);
1476         ContactsDatabaseHelper.copyLongValue(mValues, typeColumn, values,
1477                 ContactMethods.TYPE);
1478         ContactsDatabaseHelper.copyStringValue(mValues, labelColumn, values,
1479                 ContactMethods.LABEL);
1480         ContactsDatabaseHelper.copyStringValue(mValues, auxDataColumn, values,
1481                 ContactMethods.AUX_DATA);
1482     }
1483 
parseGroupValues(ContentValues values)1484     private void parseGroupValues(ContentValues values) {
1485         mValues.clear();
1486 
1487         ContactsDatabaseHelper.copyStringValue(mValues, Groups.TITLE,
1488                 values, android.provider.Contacts.Groups.NAME);
1489         ContactsDatabaseHelper.copyStringValue(mValues, Groups.NOTES,
1490                 values, android.provider.Contacts.Groups.NOTES);
1491         ContactsDatabaseHelper.copyStringValue(mValues, Groups.SYSTEM_ID,
1492                 values, android.provider.Contacts.Groups.SYSTEM_ID);
1493     }
1494 
parseExtensionValues(ContentValues values)1495     private void parseExtensionValues(ContentValues values) {
1496         ContactsDatabaseHelper.copyStringValue(mValues, ExtensionsColumns.NAME,
1497                 values, android.provider.Contacts.People.Extensions.NAME);
1498         ContactsDatabaseHelper.copyStringValue(mValues, ExtensionsColumns.VALUE,
1499                 values, android.provider.Contacts.People.Extensions.VALUE);
1500     }
1501 
findFirstDataRow(long rawContactId, String contentItemType)1502     private Uri findFirstDataRow(long rawContactId, String contentItemType) {
1503         long dataId = findFirstDataId(rawContactId, contentItemType);
1504         if (dataId == -1) {
1505             return null;
1506         }
1507 
1508         return ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
1509     }
1510 
findFirstDataId(long rawContactId, String mimeType)1511     private long findFirstDataId(long rawContactId, String mimeType) {
1512         long dataId = -1;
1513         Cursor c = mContactsProvider.query(Data.CONTENT_URI, IdQuery.COLUMNS,
1514                 Data.RAW_CONTACT_ID + "=" + rawContactId + " AND "
1515                         + Data.MIMETYPE + "='" + mimeType + "'",
1516                 null, null);
1517         try {
1518             if (c.moveToFirst()) {
1519                 dataId = c.getLong(IdQuery._ID);
1520             }
1521         } finally {
1522             c.close();
1523         }
1524         return dataId;
1525     }
1526 
1527 
delete(Uri uri, String selection, String[] selectionArgs)1528     public int delete(Uri uri, String selection, String[] selectionArgs) {
1529         final int match = sUriMatcher.match(uri);
1530         if (match == -1 || match == SETTINGS) {
1531             throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
1532         }
1533 
1534         Cursor c = query(uri, IdQuery.COLUMNS, selection, selectionArgs, null, null);
1535         if (c == null) {
1536             return 0;
1537         }
1538 
1539         int count = 0;
1540         try {
1541             while (c.moveToNext()) {
1542                 long id = c.getLong(IdQuery._ID);
1543                 count += delete(uri, match, id);
1544             }
1545         } finally {
1546             c.close();
1547         }
1548 
1549         return count;
1550     }
1551 
delete(Uri uri, int match, long id)1552     public int delete(Uri uri, int match, long id) {
1553         int count = 0;
1554         switch (match) {
1555             case PEOPLE:
1556             case PEOPLE_ID:
1557                 count = mContactsProvider.deleteRawContact(id, mDbHelper.getContactId(id), false);
1558                 break;
1559 
1560             case PEOPLE_PHOTO:
1561                 mValues.clear();
1562                 mValues.putNull(android.provider.Contacts.Photos.DATA);
1563                 updatePhoto(id, mValues);
1564                 break;
1565 
1566             case ORGANIZATIONS:
1567             case ORGANIZATIONS_ID:
1568                 count = mContactsProvider.deleteData(id, ORGANIZATION_MIME_TYPES);
1569                 break;
1570 
1571             case CONTACTMETHODS:
1572             case CONTACTMETHODS_ID:
1573                 count = mContactsProvider.deleteData(id, CONTACT_METHOD_MIME_TYPES);
1574                 break;
1575 
1576             case PHONES:
1577             case PHONES_ID:
1578                 count = mContactsProvider.deleteData(id, PHONE_MIME_TYPES);
1579                 break;
1580 
1581             case EXTENSIONS:
1582             case EXTENSIONS_ID:
1583                 count = mContactsProvider.deleteData(id, EXTENSION_MIME_TYPES);
1584                 break;
1585 
1586             case PHOTOS:
1587             case PHOTOS_ID:
1588                 count = mContactsProvider.deleteData(id, PHOTO_MIME_TYPES);
1589                 break;
1590 
1591             case GROUPMEMBERSHIP:
1592             case GROUPMEMBERSHIP_ID:
1593                 count = mContactsProvider.deleteData(id, GROUP_MEMBERSHIP_MIME_TYPES);
1594                 break;
1595 
1596             case GROUPS:
1597             case GROUPS_ID:
1598                 count = mContactsProvider.deleteGroup(uri, id, false);
1599                 break;
1600 
1601             default:
1602                 throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
1603         }
1604 
1605         return count;
1606     }
1607 
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, String limit)1608     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
1609             String sortOrder, String limit) {
1610         ensureDefaultAccount();
1611 
1612         final SQLiteDatabase db = mDbHelper.getReadableDatabase();
1613         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1614         String groupBy = null;
1615 
1616         final int match = sUriMatcher.match(uri);
1617         switch (match) {
1618             case PEOPLE: {
1619                 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1620                 qb.setProjectionMap(sPeopleProjectionMap);
1621                 applyRawContactsAccount(qb);
1622                 break;
1623             }
1624 
1625             case PEOPLE_ID:
1626                 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1627                 qb.setProjectionMap(sPeopleProjectionMap);
1628                 applyRawContactsAccount(qb);
1629                 qb.appendWhere(" AND " + People._ID + "=");
1630                 qb.appendWhere(uri.getPathSegments().get(1));
1631                 break;
1632 
1633             case PEOPLE_FILTER: {
1634                 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1635                 qb.setProjectionMap(sPeopleProjectionMap);
1636                 applyRawContactsAccount(qb);
1637                 String filterParam = uri.getPathSegments().get(2);
1638                 qb.appendWhere(" AND " + People._ID + " IN "
1639                         + getRawContactsByFilterAsNestedQuery(filterParam));
1640                 break;
1641             }
1642 
1643             case GROUP_NAME_MEMBERS:
1644                 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1645                 qb.setProjectionMap(sPeopleProjectionMap);
1646                 applyRawContactsAccount(qb);
1647                 String group = uri.getPathSegments().get(2);
1648                 qb.appendWhere(" AND " + buildGroupNameMatchWhereClause(group));
1649                 break;
1650 
1651             case GROUP_SYSTEM_ID_MEMBERS:
1652                 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
1653                 qb.setProjectionMap(sPeopleProjectionMap);
1654                 applyRawContactsAccount(qb);
1655                 String systemId = uri.getPathSegments().get(2);
1656                 qb.appendWhere(" AND " + buildGroupSystemIdMatchWhereClause(systemId));
1657                 break;
1658 
1659             case ORGANIZATIONS:
1660                 qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
1661                 qb.setProjectionMap(sOrganizationProjectionMap);
1662                 applyRawContactsAccount(qb);
1663                 break;
1664 
1665             case ORGANIZATIONS_ID:
1666                 qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
1667                 qb.setProjectionMap(sOrganizationProjectionMap);
1668                 applyRawContactsAccount(qb);
1669                 qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "=");
1670                 qb.appendWhere(uri.getPathSegments().get(1));
1671                 break;
1672 
1673             case PEOPLE_ORGANIZATIONS:
1674                 qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
1675                 qb.setProjectionMap(sOrganizationProjectionMap);
1676                 applyRawContactsAccount(qb);
1677                 qb.appendWhere(" AND " + android.provider.Contacts.Organizations.PERSON_ID + "=");
1678                 qb.appendWhere(uri.getPathSegments().get(1));
1679                 break;
1680 
1681             case PEOPLE_ORGANIZATIONS_ID:
1682                 qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
1683                 qb.setProjectionMap(sOrganizationProjectionMap);
1684                 applyRawContactsAccount(qb);
1685                 qb.appendWhere(" AND " + android.provider.Contacts.Organizations.PERSON_ID + "=");
1686                 qb.appendWhere(uri.getPathSegments().get(1));
1687                 qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "=");
1688                 qb.appendWhere(uri.getPathSegments().get(3));
1689                 break;
1690 
1691             case CONTACTMETHODS:
1692                 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
1693                 qb.setProjectionMap(sContactMethodProjectionMap);
1694                 applyRawContactsAccount(qb);
1695                 break;
1696 
1697             case CONTACTMETHODS_ID:
1698                 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
1699                 qb.setProjectionMap(sContactMethodProjectionMap);
1700                 applyRawContactsAccount(qb);
1701                 qb.appendWhere(" AND " + ContactMethods._ID + "=");
1702                 qb.appendWhere(uri.getPathSegments().get(1));
1703                 break;
1704 
1705             case CONTACTMETHODS_EMAIL:
1706                 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
1707                 qb.setProjectionMap(sContactMethodProjectionMap);
1708                 applyRawContactsAccount(qb);
1709                 qb.appendWhere(" AND " + ContactMethods.KIND + "="
1710                         + android.provider.Contacts.KIND_EMAIL);
1711                 break;
1712 
1713             case PEOPLE_CONTACTMETHODS:
1714                 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
1715                 qb.setProjectionMap(sContactMethodProjectionMap);
1716                 applyRawContactsAccount(qb);
1717                 qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "=");
1718                 qb.appendWhere(uri.getPathSegments().get(1));
1719                 qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL");
1720                 break;
1721 
1722             case PEOPLE_CONTACTMETHODS_ID:
1723                 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
1724                 qb.setProjectionMap(sContactMethodProjectionMap);
1725                 applyRawContactsAccount(qb);
1726                 qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "=");
1727                 qb.appendWhere(uri.getPathSegments().get(1));
1728                 qb.appendWhere(" AND " + ContactMethods._ID + "=");
1729                 qb.appendWhere(uri.getPathSegments().get(3));
1730                 qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL");
1731                 break;
1732 
1733             case PHONES:
1734                 qb.setTables(LegacyTables.PHONES + " phones");
1735                 qb.setProjectionMap(sPhoneProjectionMap);
1736                 applyRawContactsAccount(qb);
1737                 break;
1738 
1739             case PHONES_ID:
1740                 qb.setTables(LegacyTables.PHONES + " phones");
1741                 qb.setProjectionMap(sPhoneProjectionMap);
1742                 applyRawContactsAccount(qb);
1743                 qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "=");
1744                 qb.appendWhere(uri.getPathSegments().get(1));
1745                 break;
1746 
1747             case PHONES_FILTER:
1748                 qb.setTables(LegacyTables.PHONES + " phones");
1749                 qb.setProjectionMap(sPhoneProjectionMap);
1750                 applyRawContactsAccount(qb);
1751                 if (uri.getPathSegments().size() > 2) {
1752                     String filterParam = uri.getLastPathSegment();
1753                     qb.appendWhere(" AND person =");
1754                     qb.appendWhere(mDbHelper.buildPhoneLookupAsNestedQuery(filterParam));
1755                     qb.setDistinct(true);
1756                 }
1757                 break;
1758 
1759             case PEOPLE_PHONES:
1760                 qb.setTables(LegacyTables.PHONES + " phones");
1761                 qb.setProjectionMap(sPhoneProjectionMap);
1762                 applyRawContactsAccount(qb);
1763                 qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "=");
1764                 qb.appendWhere(uri.getPathSegments().get(1));
1765                 break;
1766 
1767             case PEOPLE_PHONES_ID:
1768                 qb.setTables(LegacyTables.PHONES + " phones");
1769                 qb.setProjectionMap(sPhoneProjectionMap);
1770                 applyRawContactsAccount(qb);
1771                 qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "=");
1772                 qb.appendWhere(uri.getPathSegments().get(1));
1773                 qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "=");
1774                 qb.appendWhere(uri.getPathSegments().get(3));
1775                 break;
1776 
1777             case EXTENSIONS:
1778                 qb.setTables(LegacyTables.EXTENSIONS + " extensions");
1779                 qb.setProjectionMap(sExtensionProjectionMap);
1780                 applyRawContactsAccount(qb);
1781                 break;
1782 
1783             case EXTENSIONS_ID:
1784                 qb.setTables(LegacyTables.EXTENSIONS + " extensions");
1785                 qb.setProjectionMap(sExtensionProjectionMap);
1786                 applyRawContactsAccount(qb);
1787                 qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "=");
1788                 qb.appendWhere(uri.getPathSegments().get(1));
1789                 break;
1790 
1791             case PEOPLE_EXTENSIONS:
1792                 qb.setTables(LegacyTables.EXTENSIONS + " extensions");
1793                 qb.setProjectionMap(sExtensionProjectionMap);
1794                 applyRawContactsAccount(qb);
1795                 qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "=");
1796                 qb.appendWhere(uri.getPathSegments().get(1));
1797                 break;
1798 
1799             case PEOPLE_EXTENSIONS_ID:
1800                 qb.setTables(LegacyTables.EXTENSIONS + " extensions");
1801                 qb.setProjectionMap(sExtensionProjectionMap);
1802                 applyRawContactsAccount(qb);
1803                 qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "=");
1804                 qb.appendWhere(uri.getPathSegments().get(1));
1805                 qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "=");
1806                 qb.appendWhere(uri.getPathSegments().get(3));
1807                 break;
1808 
1809             case GROUPS:
1810                 qb.setTables(LegacyTables.GROUPS + " groups");
1811                 qb.setProjectionMap(sGroupProjectionMap);
1812                 applyGroupAccount(qb);
1813                 break;
1814 
1815             case GROUPS_ID:
1816                 qb.setTables(LegacyTables.GROUPS + " groups");
1817                 qb.setProjectionMap(sGroupProjectionMap);
1818                 applyGroupAccount(qb);
1819                 qb.appendWhere(" AND " + android.provider.Contacts.Groups._ID + "=");
1820                 qb.appendWhere(uri.getPathSegments().get(1));
1821                 break;
1822 
1823             case GROUPMEMBERSHIP:
1824                 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
1825                 qb.setProjectionMap(sGroupMembershipProjectionMap);
1826                 applyRawContactsAccount(qb);
1827                 break;
1828 
1829             case GROUPMEMBERSHIP_ID:
1830                 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
1831                 qb.setProjectionMap(sGroupMembershipProjectionMap);
1832                 applyRawContactsAccount(qb);
1833                 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "=");
1834                 qb.appendWhere(uri.getPathSegments().get(1));
1835                 break;
1836 
1837             case PEOPLE_GROUPMEMBERSHIP:
1838                 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
1839                 qb.setProjectionMap(sGroupMembershipProjectionMap);
1840                 applyRawContactsAccount(qb);
1841                 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "=");
1842                 qb.appendWhere(uri.getPathSegments().get(1));
1843                 break;
1844 
1845             case PEOPLE_GROUPMEMBERSHIP_ID:
1846                 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
1847                 qb.setProjectionMap(sGroupMembershipProjectionMap);
1848                 applyRawContactsAccount(qb);
1849                 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "=");
1850                 qb.appendWhere(uri.getPathSegments().get(1));
1851                 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "=");
1852                 qb.appendWhere(uri.getPathSegments().get(3));
1853                 break;
1854 
1855             case PEOPLE_PHOTO:
1856                 qb.setTables(LegacyTables.PHOTOS + " photos");
1857                 qb.setProjectionMap(sPhotoProjectionMap);
1858                 applyRawContactsAccount(qb);
1859                 qb.appendWhere(" AND " + android.provider.Contacts.Photos.PERSON_ID + "=");
1860                 qb.appendWhere(uri.getPathSegments().get(1));
1861                 limit = "1";
1862                 break;
1863 
1864             case PHOTOS:
1865                 qb.setTables(LegacyTables.PHOTOS + " photos");
1866                 qb.setProjectionMap(sPhotoProjectionMap);
1867                 applyRawContactsAccount(qb);
1868                 break;
1869 
1870             case PHOTOS_ID:
1871                 qb.setTables(LegacyTables.PHOTOS + " photos");
1872                 qb.setProjectionMap(sPhotoProjectionMap);
1873                 applyRawContactsAccount(qb);
1874                 qb.appendWhere(" AND " + android.provider.Contacts.Photos._ID + "=");
1875                 qb.appendWhere(uri.getPathSegments().get(1));
1876                 break;
1877 
1878             case SEARCH_SUGGESTIONS:
1879                 return mGlobalSearchSupport.handleSearchSuggestionsQuery(
1880                         db, uri, projection, limit, null);
1881 
1882             case SEARCH_SHORTCUT: {
1883                 String lookupKey = uri.getLastPathSegment();
1884                 String filter = ContactsProvider2.getQueryParameter(uri, "filter");
1885                 return mGlobalSearchSupport.handleSearchShortcutRefresh(
1886                         db, projection, lookupKey, filter, null);
1887             }
1888 
1889             case LIVE_FOLDERS_PEOPLE:
1890                 return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_URI,
1891                         projection, selection, selectionArgs, sortOrder);
1892 
1893             case LIVE_FOLDERS_PEOPLE_WITH_PHONES:
1894                 return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_WITH_PHONES_URI,
1895                         projection, selection, selectionArgs, sortOrder);
1896 
1897             case LIVE_FOLDERS_PEOPLE_FAVORITES:
1898                 return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_FAVORITES_URI,
1899                         projection, selection, selectionArgs, sortOrder);
1900 
1901             case LIVE_FOLDERS_PEOPLE_GROUP_NAME:
1902                 return mContactsProvider.query(Uri.withAppendedPath(LIVE_FOLDERS_CONTACTS_URI,
1903                         Uri.encode(uri.getLastPathSegment())),
1904                         projection, selection, selectionArgs, sortOrder);
1905 
1906             case DELETED_PEOPLE:
1907             case DELETED_GROUPS:
1908                 throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
1909 
1910             case SETTINGS:
1911                 copySettingsToLegacySettings();
1912                 qb.setTables(LegacyTables.SETTINGS);
1913                 break;
1914 
1915             default:
1916                 throw new IllegalArgumentException(mDbHelper.exceptionMessage(uri));
1917         }
1918 
1919         // Perform the query and set the notification uri
1920         final Cursor c = qb.query(db, projection, selection, selectionArgs,
1921                 groupBy, null, sortOrder, limit);
1922         if (c != null) {
1923             c.setNotificationUri(mContext.getContentResolver(),
1924                     android.provider.Contacts.CONTENT_URI);
1925         }
1926         return c;
1927     }
1928 
applyRawContactsAccount(SQLiteQueryBuilder qb)1929     private void applyRawContactsAccount(SQLiteQueryBuilder qb) {
1930         StringBuilder sb = new StringBuilder();
1931         appendRawContactsAccount(sb);
1932         qb.appendWhere(sb.toString());
1933     }
1934 
appendRawContactsAccount(StringBuilder sb)1935     private void appendRawContactsAccount(StringBuilder sb) {
1936         if (mAccount != null) {
1937             sb.append(RawContacts.ACCOUNT_NAME + "=");
1938             DatabaseUtils.appendEscapedSQLString(sb, mAccount.name);
1939             sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
1940             DatabaseUtils.appendEscapedSQLString(sb, mAccount.type);
1941         } else {
1942             sb.append(RawContacts.ACCOUNT_NAME + " IS NULL" +
1943                     " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL");
1944         }
1945     }
1946 
applyGroupAccount(SQLiteQueryBuilder qb)1947     private void applyGroupAccount(SQLiteQueryBuilder qb) {
1948         StringBuilder sb = new StringBuilder();
1949         appendGroupAccount(sb);
1950         qb.appendWhere(sb.toString());
1951     }
1952 
appendGroupAccount(StringBuilder sb)1953     private void appendGroupAccount(StringBuilder sb) {
1954         if (mAccount != null) {
1955             sb.append(Groups.ACCOUNT_NAME + "=");
1956             DatabaseUtils.appendEscapedSQLString(sb, mAccount.name);
1957             sb.append(" AND " + Groups.ACCOUNT_TYPE + "=");
1958             DatabaseUtils.appendEscapedSQLString(sb, mAccount.type);
1959         } else {
1960             sb.append(Groups.ACCOUNT_NAME + " IS NULL" +
1961                     " AND " + Groups.ACCOUNT_TYPE + " IS NULL");
1962         }
1963     }
1964 
1965     /**
1966      * Build a WHERE clause that restricts the query to match people that are a member of
1967      * a group with a particular name. The projection map of the query must include
1968      * {@link People#_ID}.
1969      *
1970      * @param groupName The name of the group
1971      * @return The where clause.
1972      */
buildGroupNameMatchWhereClause(String groupName)1973     private String buildGroupNameMatchWhereClause(String groupName) {
1974         return "people._id IN "
1975                 + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
1976                 + " FROM " + Tables.DATA_JOIN_MIMETYPES
1977                 + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
1978                         + "' AND " + GroupMembership.GROUP_ROW_ID + "="
1979                                 + "(SELECT " + Tables.GROUPS + "." + Groups._ID
1980                                 + " FROM " + Tables.GROUPS
1981                                 + " WHERE " + Groups.TITLE + "="
1982                                         + DatabaseUtils.sqlEscapeString(groupName) + "))";
1983     }
1984 
1985     /**
1986      * Build a WHERE clause that restricts the query to match people that are a member of
1987      * a group with a particular system id. The projection map of the query must include
1988      * {@link People#_ID}.
1989      *
1990      * @param groupName The name of the group
1991      * @return The where clause.
1992      */
buildGroupSystemIdMatchWhereClause(String systemId)1993     private String buildGroupSystemIdMatchWhereClause(String systemId) {
1994         return "people._id IN "
1995                 + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
1996                 + " FROM " + Tables.DATA_JOIN_MIMETYPES
1997                 + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
1998                         + "' AND " + GroupMembership.GROUP_ROW_ID + "="
1999                                 + "(SELECT " + Tables.GROUPS + "." + Groups._ID
2000                                 + " FROM " + Tables.GROUPS
2001                                 + " WHERE " + Groups.SYSTEM_ID + "="
2002                                         + DatabaseUtils.sqlEscapeString(systemId) + "))";
2003     }
2004 
getRawContactsByFilterAsNestedQuery(String filterParam)2005     private String getRawContactsByFilterAsNestedQuery(String filterParam) {
2006         StringBuilder sb = new StringBuilder();
2007         String normalizedName = NameNormalizer.normalize(filterParam);
2008         if (TextUtils.isEmpty(normalizedName)) {
2009             // Effectively an empty IN clause - SQL syntax does not allow an actual empty list here
2010             sb.append("(0)");
2011         } else {
2012             sb.append("(" +
2013                     "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
2014                     " FROM " + Tables.NAME_LOOKUP +
2015                     " WHERE " + NameLookupColumns.NORMALIZED_NAME +
2016                     " GLOB '");
2017             // Should not use a "?" argument placeholder here, because
2018             // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME.
2019             sb.append(normalizedName);
2020             sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
2021                     + NameLookupType.NAME_COLLATION_KEY + ","
2022                     + NameLookupType.NICKNAME);
2023             if (true) {
2024                 sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
2025             }
2026             sb.append("))");
2027         }
2028         return sb.toString();
2029     }
2030 
2031     /**
2032      * Called when a change has been made.
2033      *
2034      * @param uri the uri that the change was made to
2035      */
onChange(Uri uri)2036     private void onChange(Uri uri) {
2037         mContext.getContentResolver().notifyChange(android.provider.Contacts.CONTENT_URI, null);
2038     }
2039 
getType(Uri uri)2040     public String getType(Uri uri) {
2041         int match = sUriMatcher.match(uri);
2042         switch (match) {
2043             case EXTENSIONS:
2044             case PEOPLE_EXTENSIONS:
2045                 return Extensions.CONTENT_TYPE;
2046             case EXTENSIONS_ID:
2047             case PEOPLE_EXTENSIONS_ID:
2048                 return Extensions.CONTENT_ITEM_TYPE;
2049             case PEOPLE:
2050                 return "vnd.android.cursor.dir/person";
2051             case PEOPLE_ID:
2052                 return "vnd.android.cursor.item/person";
2053             case PEOPLE_PHONES:
2054                 return "vnd.android.cursor.dir/phone";
2055             case PEOPLE_PHONES_ID:
2056                 return "vnd.android.cursor.item/phone";
2057             case PEOPLE_CONTACTMETHODS:
2058                 return "vnd.android.cursor.dir/contact-methods";
2059             case PEOPLE_CONTACTMETHODS_ID:
2060                 return getContactMethodType(uri);
2061             case PHONES:
2062                 return "vnd.android.cursor.dir/phone";
2063             case PHONES_ID:
2064                 return "vnd.android.cursor.item/phone";
2065             case PHONES_FILTER:
2066                 return "vnd.android.cursor.dir/phone";
2067             case PHOTOS_ID:
2068                 return "vnd.android.cursor.item/photo";
2069             case PHOTOS:
2070                 return "vnd.android.cursor.dir/photo";
2071             case PEOPLE_PHOTO:
2072                 return "vnd.android.cursor.item/photo";
2073             case CONTACTMETHODS:
2074                 return "vnd.android.cursor.dir/contact-methods";
2075             case CONTACTMETHODS_ID:
2076                 return getContactMethodType(uri);
2077             case ORGANIZATIONS:
2078                 return "vnd.android.cursor.dir/organizations";
2079             case ORGANIZATIONS_ID:
2080                 return "vnd.android.cursor.item/organization";
2081             case SEARCH_SUGGESTIONS:
2082                 return SearchManager.SUGGEST_MIME_TYPE;
2083             case SEARCH_SHORTCUT:
2084                 return SearchManager.SHORTCUT_MIME_TYPE;
2085             default:
2086                 throw new IllegalArgumentException(mDbHelper.exceptionMessage(uri));
2087         }
2088     }
2089 
getContactMethodType(Uri url)2090     private String getContactMethodType(Uri url) {
2091         String mime = null;
2092 
2093         Cursor c = query(url, new String[] {ContactMethods.KIND}, null, null, null, null);
2094         if (c != null) {
2095             try {
2096                 if (c.moveToFirst()) {
2097                     int kind = c.getInt(0);
2098                     switch (kind) {
2099                     case android.provider.Contacts.KIND_EMAIL:
2100                         mime = "vnd.android.cursor.item/email";
2101                         break;
2102 
2103                     case android.provider.Contacts.KIND_IM:
2104                         mime = "vnd.android.cursor.item/jabber-im";
2105                         break;
2106 
2107                     case android.provider.Contacts.KIND_POSTAL:
2108                         mime = "vnd.android.cursor.item/postal-address";
2109                         break;
2110                     }
2111                 }
2112             } finally {
2113                 c.close();
2114             }
2115         }
2116         return mime;
2117     }
2118 }
2119