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