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