LegacyApiSupport.java revision 0a185cdcb65d1beb2a295fffbe2ae11a6a2c097f
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16package com.android.providers.contacts; 17 18import com.android.providers.contacts.OpenHelper.DataColumns; 19import com.android.providers.contacts.OpenHelper.ExtensionsColumns; 20import com.android.providers.contacts.OpenHelper.GroupsColumns; 21import com.android.providers.contacts.OpenHelper.MimetypesColumns; 22import com.android.providers.contacts.OpenHelper.PhoneColumns; 23import com.android.providers.contacts.OpenHelper.PresenceColumns; 24import com.android.providers.contacts.OpenHelper.RawContactsColumns; 25import com.android.providers.contacts.OpenHelper.StatusUpdatesColumns; 26import com.android.providers.contacts.OpenHelper.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 OpenHelper mOpenHelper; 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, OpenHelper openHelper, 483 ContactsProvider2 contactsProvider, GlobalSearchSupport globalSearchSupport) { 484 mContext = context; 485 mContactsProvider = contactsProvider; 486 mOpenHelper = openHelper; 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 = mOpenHelper.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 = mOpenHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE); 509 mMimetypeIm = mOpenHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE); 510 mMimetypePostal = mOpenHelper.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 OpenHelper.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 = mOpenHelper.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 OpenHelper.copyStringValue(mValues, LegacyPhotoData.LOCAL_VERSION, 1160 values, android.provider.Contacts.Photos.LOCAL_VERSION); 1161 OpenHelper.copyStringValue(mValues, LegacyPhotoData.DOWNLOAD_REQUIRED, 1162 values, android.provider.Contacts.Photos.DOWNLOAD_REQUIRED); 1163 OpenHelper.copyStringValue(mValues, LegacyPhotoData.EXISTS_ON_SERVER, 1164 values, android.provider.Contacts.Photos.EXISTS_ON_SERVER); 1165 OpenHelper.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 OpenHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE, 1186 values, People.CUSTOM_RINGTONE); 1187 OpenHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL, 1188 values, People.SEND_TO_VOICEMAIL); 1189 OpenHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED, 1190 values, People.LAST_TIME_CONTACTED); 1191 OpenHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED, 1192 values, People.TIMES_CONTACTED); 1193 OpenHelper.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 OpenHelper.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 OpenHelper.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 OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY, 1224 values, android.provider.Contacts.Organizations.ISPRIMARY); 1225 1226 OpenHelper.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 OpenHelper.copyLongValue(mValues, Organization.TYPE, 1231 values, android.provider.Contacts.Organizations.TYPE); 1232 1233 OpenHelper.copyStringValue(mValues, Organization.LABEL, 1234 values, android.provider.Contacts.Organizations.LABEL); 1235 OpenHelper.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 OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY, 1245 values, android.provider.Contacts.Phones.ISPRIMARY); 1246 1247 OpenHelper.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 OpenHelper.copyLongValue(mValues, Phone.TYPE, 1252 values, android.provider.Contacts.Phones.TYPE); 1253 1254 OpenHelper.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 OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY, values, ContactMethods.ISPRIMARY); 1262 1263 switch (kind) { 1264 case android.provider.Contacts.KIND_EMAIL: { 1265 copyCommonFields(values, Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL, 1266 Data.DATA14); 1267 OpenHelper.copyStringValue(mValues, Email.DATA, values, ContactMethods.DATA); 1268 break; 1269 } 1270 1271 case android.provider.Contacts.KIND_IM: { 1272 String protocol = values.getAsString(ContactMethods.DATA); 1273 if (protocol.startsWith("pre:")) { 1274 mValues.put(Im.PROTOCOL, Integer.parseInt(protocol.substring(4))); 1275 } else if (protocol.startsWith("custom:")) { 1276 mValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM); 1277 mValues.put(Im.CUSTOM_PROTOCOL, protocol.substring(7)); 1278 } 1279 1280 copyCommonFields(values, Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL, Data.DATA14); 1281 break; 1282 } 1283 1284 case android.provider.Contacts.KIND_POSTAL: { 1285 copyCommonFields(values, StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, 1286 StructuredPostal.LABEL, Data.DATA14); 1287 OpenHelper.copyStringValue(mValues, StructuredPostal.FORMATTED_ADDRESS, values, 1288 ContactMethods.DATA); 1289 break; 1290 } 1291 } 1292 } 1293 1294 private void copyCommonFields(ContentValues values, String mimeType, String typeColumn, 1295 String labelColumn, String auxDataColumn) { 1296 mValues.put(Data.MIMETYPE, mimeType); 1297 OpenHelper.copyLongValue(mValues, typeColumn, values, ContactMethods.TYPE); 1298 OpenHelper.copyStringValue(mValues, labelColumn, values, ContactMethods.LABEL); 1299 OpenHelper.copyStringValue(mValues, auxDataColumn, values, ContactMethods.AUX_DATA); 1300 } 1301 1302 private void parseGroupValues(ContentValues values) { 1303 mValues.clear(); 1304 1305 OpenHelper.copyStringValue(mValues, Groups.TITLE, 1306 values, android.provider.Contacts.Groups.NAME); 1307 OpenHelper.copyStringValue(mValues, Groups.NOTES, 1308 values, android.provider.Contacts.Groups.NOTES); 1309 OpenHelper.copyStringValue(mValues, Groups.SYSTEM_ID, 1310 values, android.provider.Contacts.Groups.SYSTEM_ID); 1311 } 1312 1313 private void parseExtensionValues(ContentValues values) { 1314 OpenHelper.copyStringValue(mValues, ExtensionsColumns.NAME, 1315 values, android.provider.Contacts.People.Extensions.NAME); 1316 OpenHelper.copyStringValue(mValues, ExtensionsColumns.VALUE, 1317 values, android.provider.Contacts.People.Extensions.VALUE); 1318 } 1319 1320 private Uri findFirstDataRow(long rawContactId, String contentItemType) { 1321 long dataId = findFirstDataId(rawContactId, contentItemType); 1322 if (dataId == -1) { 1323 return null; 1324 } 1325 1326 return ContentUris.withAppendedId(Data.CONTENT_URI, dataId); 1327 } 1328 1329 private long findFirstDataId(long rawContactId, String mimeType) { 1330 long dataId = -1; 1331 Cursor c = mContactsProvider.query(Data.CONTENT_URI, IdQuery.COLUMNS, 1332 Data.RAW_CONTACT_ID + "=" + rawContactId + " AND " 1333 + Data.MIMETYPE + "='" + mimeType + "'", 1334 null, null); 1335 try { 1336 if (c.moveToFirst()) { 1337 dataId = c.getLong(IdQuery._ID); 1338 } 1339 } finally { 1340 c.close(); 1341 } 1342 return dataId; 1343 } 1344 1345 1346 public int delete(Uri uri, String selection, String[] selectionArgs) { 1347 Cursor c = query(uri, IdQuery.COLUMNS, selection, selectionArgs, null, null); 1348 if (c == null) { 1349 return 0; 1350 } 1351 1352 int count = 0; 1353 try { 1354 while (c.moveToNext()) { 1355 long id = c.getLong(IdQuery._ID); 1356 count += delete(uri, id); 1357 } 1358 } finally { 1359 c.close(); 1360 } 1361 1362 return count; 1363 } 1364 1365 public int delete(Uri uri, long id) { 1366 final int match = sUriMatcher.match(uri); 1367 int count = 0; 1368 switch (match) { 1369 case PEOPLE: 1370 case PEOPLE_ID: 1371 count = mContactsProvider.deleteRawContact(ContentUris.parseId(uri), false); 1372 break; 1373 1374 case PEOPLE_PHOTO: 1375 mValues.clear(); 1376 mValues.putNull(android.provider.Contacts.Photos.DATA); 1377 updatePhoto(id, mValues); 1378 break; 1379 1380 case ORGANIZATIONS: 1381 case ORGANIZATIONS_ID: 1382 count = mContactsProvider.deleteData(ContentUris.parseId(uri), 1383 ORGANIZATION_MIME_TYPES); 1384 break; 1385 1386 case CONTACTMETHODS: 1387 case CONTACTMETHODS_ID: 1388 count = mContactsProvider.deleteData(ContentUris.parseId(uri), 1389 CONTACT_METHOD_MIME_TYPES); 1390 break; 1391 1392 1393 case PHONES: 1394 case PHONES_ID: 1395 count = mContactsProvider.deleteData(ContentUris.parseId(uri), 1396 PHONE_MIME_TYPES); 1397 break; 1398 1399 case EXTENSIONS: 1400 case EXTENSIONS_ID: 1401 count = mContactsProvider.deleteData(ContentUris.parseId(uri), 1402 EXTENSION_MIME_TYPES); 1403 break; 1404 1405 case PHOTOS: 1406 case PHOTOS_ID: 1407 count = mContactsProvider.deleteData(ContentUris.parseId(uri), 1408 PHOTO_MIME_TYPES); 1409 1410 break; 1411 1412 default: 1413 throw new UnsupportedOperationException("Unknown uri: " + uri); 1414 } 1415 1416 return count; 1417 } 1418 1419 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 1420 String sortOrder, String limit) { 1421 ensureDefaultAccount(); 1422 1423 final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 1424 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 1425 String groupBy = null; 1426 1427 final int match = sUriMatcher.match(uri); 1428 switch (match) { 1429 case PEOPLE: { 1430 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE); 1431 qb.setProjectionMap(sPeopleProjectionMap); 1432 applyRawContactsAccount(qb); 1433 break; 1434 } 1435 1436 case PEOPLE_ID: 1437 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE); 1438 qb.setProjectionMap(sPeopleProjectionMap); 1439 applyRawContactsAccount(qb); 1440 qb.appendWhere(" AND " + People._ID + "="); 1441 qb.appendWhere(uri.getPathSegments().get(1)); 1442 break; 1443 1444 case PEOPLE_FILTER: { 1445 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE); 1446 qb.setProjectionMap(sPeopleProjectionMap); 1447 applyRawContactsAccount(qb); 1448 String filterParam = uri.getPathSegments().get(2); 1449 qb.appendWhere(" AND " + People._ID + " IN " 1450 + mContactsProvider.getRawContactsByFilterAsNestedQuery(filterParam)); 1451 break; 1452 } 1453 1454 case GROUP_NAME_MEMBERS: 1455 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE); 1456 qb.setProjectionMap(sPeopleProjectionMap); 1457 applyRawContactsAccount(qb); 1458 String group = uri.getPathSegments().get(2); 1459 qb.appendWhere(" AND " + buildGroupNameMatchWhereClause(group)); 1460 break; 1461 1462 case ORGANIZATIONS: 1463 qb.setTables(LegacyTables.ORGANIZATIONS + " organizations"); 1464 qb.setProjectionMap(sOrganizationProjectionMap); 1465 applyRawContactsAccount(qb); 1466 break; 1467 1468 case ORGANIZATIONS_ID: 1469 qb.setTables(LegacyTables.ORGANIZATIONS + " organizations"); 1470 qb.setProjectionMap(sOrganizationProjectionMap); 1471 applyRawContactsAccount(qb); 1472 qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "="); 1473 qb.appendWhere(uri.getPathSegments().get(1)); 1474 break; 1475 1476 case CONTACTMETHODS: 1477 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods"); 1478 qb.setProjectionMap(sContactMethodProjectionMap); 1479 applyRawContactsAccount(qb); 1480 break; 1481 1482 case CONTACTMETHODS_ID: 1483 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods"); 1484 qb.setProjectionMap(sContactMethodProjectionMap); 1485 applyRawContactsAccount(qb); 1486 qb.appendWhere(" AND " + ContactMethods._ID + "="); 1487 qb.appendWhere(uri.getPathSegments().get(1)); 1488 break; 1489 1490 case CONTACTMETHODS_EMAIL: 1491 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods"); 1492 qb.setProjectionMap(sContactMethodProjectionMap); 1493 applyRawContactsAccount(qb); 1494 qb.appendWhere(" AND " + ContactMethods.KIND + "=" 1495 + android.provider.Contacts.KIND_EMAIL); 1496 break; 1497 1498 case PEOPLE_CONTACTMETHODS: 1499 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods"); 1500 qb.setProjectionMap(sContactMethodProjectionMap); 1501 applyRawContactsAccount(qb); 1502 qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "="); 1503 qb.appendWhere(uri.getPathSegments().get(1)); 1504 qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL"); 1505 break; 1506 1507 case PEOPLE_CONTACTMETHODS_ID: 1508 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods"); 1509 qb.setProjectionMap(sContactMethodProjectionMap); 1510 applyRawContactsAccount(qb); 1511 qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "="); 1512 qb.appendWhere(uri.getPathSegments().get(1)); 1513 qb.appendWhere(" AND " + ContactMethods._ID + "="); 1514 qb.appendWhere(uri.getPathSegments().get(3)); 1515 qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL"); 1516 break; 1517 1518 case PHONES: 1519 qb.setTables(LegacyTables.PHONES + " phones"); 1520 qb.setProjectionMap(sPhoneProjectionMap); 1521 applyRawContactsAccount(qb); 1522 break; 1523 1524 case PHONES_ID: 1525 qb.setTables(LegacyTables.PHONES + " phones"); 1526 qb.setProjectionMap(sPhoneProjectionMap); 1527 applyRawContactsAccount(qb); 1528 qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "="); 1529 qb.appendWhere(uri.getPathSegments().get(1)); 1530 break; 1531 1532 case PHONES_FILTER: 1533 qb.setTables(LegacyTables.PHONES + " phones"); 1534 qb.setProjectionMap(sPhoneProjectionMap); 1535 applyRawContactsAccount(qb); 1536 if (uri.getPathSegments().size() > 2) { 1537 String filterParam = uri.getLastPathSegment(); 1538 qb.appendWhere(" AND person ="); 1539 qb.appendWhere(mOpenHelper.buildPhoneLookupAsNestedQuery(filterParam)); 1540 } 1541 break; 1542 1543 case PEOPLE_PHONES: 1544 qb.setTables(LegacyTables.PHONES + " phones"); 1545 qb.setProjectionMap(sPhoneProjectionMap); 1546 applyRawContactsAccount(qb); 1547 qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "="); 1548 qb.appendWhere(uri.getPathSegments().get(1)); 1549 break; 1550 1551 case PEOPLE_PHONES_ID: 1552 qb.setTables(LegacyTables.PHONES + " phones"); 1553 qb.setProjectionMap(sPhoneProjectionMap); 1554 applyRawContactsAccount(qb); 1555 qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "="); 1556 qb.appendWhere(uri.getPathSegments().get(1)); 1557 qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "="); 1558 qb.appendWhere(uri.getPathSegments().get(3)); 1559 break; 1560 1561 case EXTENSIONS: 1562 qb.setTables(LegacyTables.EXTENSIONS + " extensions"); 1563 qb.setProjectionMap(sExtensionProjectionMap); 1564 applyRawContactsAccount(qb); 1565 break; 1566 1567 case EXTENSIONS_ID: 1568 qb.setTables(LegacyTables.EXTENSIONS + " extensions"); 1569 qb.setProjectionMap(sExtensionProjectionMap); 1570 applyRawContactsAccount(qb); 1571 qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "="); 1572 qb.appendWhere(uri.getPathSegments().get(1)); 1573 break; 1574 1575 case PEOPLE_EXTENSIONS: 1576 qb.setTables(LegacyTables.EXTENSIONS + " extensions"); 1577 qb.setProjectionMap(sExtensionProjectionMap); 1578 applyRawContactsAccount(qb); 1579 qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "="); 1580 qb.appendWhere(uri.getPathSegments().get(1)); 1581 break; 1582 1583 case PEOPLE_EXTENSIONS_ID: 1584 qb.setTables(LegacyTables.EXTENSIONS + " extensions"); 1585 qb.setProjectionMap(sExtensionProjectionMap); 1586 applyRawContactsAccount(qb); 1587 qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "="); 1588 qb.appendWhere(uri.getPathSegments().get(1)); 1589 qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "="); 1590 qb.appendWhere(uri.getPathSegments().get(3)); 1591 break; 1592 1593 case GROUPS: 1594 qb.setTables(LegacyTables.GROUPS + " groups"); 1595 qb.setProjectionMap(sGroupProjectionMap); 1596 applyGroupAccount(qb); 1597 break; 1598 1599 case GROUPS_ID: 1600 qb.setTables(LegacyTables.GROUPS + " groups"); 1601 qb.setProjectionMap(sGroupProjectionMap); 1602 applyGroupAccount(qb); 1603 qb.appendWhere(" AND " + android.provider.Contacts.Groups._ID + "="); 1604 qb.appendWhere(uri.getPathSegments().get(1)); 1605 break; 1606 1607 case GROUPMEMBERSHIP: 1608 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership"); 1609 qb.setProjectionMap(sGroupMembershipProjectionMap); 1610 applyRawContactsAccount(qb); 1611 break; 1612 1613 case GROUPMEMBERSHIP_ID: 1614 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership"); 1615 qb.setProjectionMap(sGroupMembershipProjectionMap); 1616 applyRawContactsAccount(qb); 1617 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "="); 1618 qb.appendWhere(uri.getPathSegments().get(1)); 1619 break; 1620 1621 case PEOPLE_GROUPMEMBERSHIP: 1622 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership"); 1623 qb.setProjectionMap(sGroupMembershipProjectionMap); 1624 applyRawContactsAccount(qb); 1625 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "="); 1626 qb.appendWhere(uri.getPathSegments().get(1)); 1627 break; 1628 1629 case PEOPLE_GROUPMEMBERSHIP_ID: 1630 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership"); 1631 qb.setProjectionMap(sGroupMembershipProjectionMap); 1632 applyRawContactsAccount(qb); 1633 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "="); 1634 qb.appendWhere(uri.getPathSegments().get(1)); 1635 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "="); 1636 qb.appendWhere(uri.getPathSegments().get(3)); 1637 break; 1638 1639 case PEOPLE_PHOTO: 1640 qb.setTables(LegacyTables.PHOTOS + " photos"); 1641 qb.setProjectionMap(sPhotoProjectionMap); 1642 applyRawContactsAccount(qb); 1643 qb.appendWhere(" AND " + android.provider.Contacts.Photos.PERSON_ID + "="); 1644 qb.appendWhere(uri.getPathSegments().get(1)); 1645 limit = "1"; 1646 break; 1647 1648 case PHOTOS: 1649 qb.setTables(LegacyTables.PHOTOS + " photos"); 1650 qb.setProjectionMap(sPhotoProjectionMap); 1651 applyRawContactsAccount(qb); 1652 break; 1653 1654 case PHOTOS_ID: 1655 qb.setTables(LegacyTables.PHOTOS + " photos"); 1656 qb.setProjectionMap(sPhotoProjectionMap); 1657 applyRawContactsAccount(qb); 1658 qb.appendWhere(" AND " + android.provider.Contacts.Photos._ID + "="); 1659 qb.appendWhere(uri.getPathSegments().get(1)); 1660 break; 1661 1662 case SEARCH_SUGGESTIONS: 1663 1664 // No legacy compatibility for search suggestions 1665 return mGlobalSearchSupport.handleSearchSuggestionsQuery(db, uri, limit); 1666 1667 case SEARCH_SHORTCUT: { 1668 long contactId = ContentUris.parseId(uri); 1669 return mGlobalSearchSupport.handleSearchShortcutRefresh(db, contactId, projection); 1670 } 1671 1672 case LIVE_FOLDERS_PEOPLE: 1673 return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_URI, 1674 projection, selection, selectionArgs, sortOrder); 1675 1676 case LIVE_FOLDERS_PEOPLE_WITH_PHONES: 1677 return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_WITH_PHONES_URI, 1678 projection, selection, selectionArgs, sortOrder); 1679 1680 case LIVE_FOLDERS_PEOPLE_FAVORITES: 1681 return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_FAVORITES_URI, 1682 projection, selection, selectionArgs, sortOrder); 1683 1684 case LIVE_FOLDERS_PEOPLE_GROUP_NAME: 1685 return mContactsProvider.query(Uri.withAppendedPath(LIVE_FOLDERS_CONTACTS_URI, 1686 Uri.encode(uri.getLastPathSegment())), 1687 projection, selection, selectionArgs, sortOrder); 1688 1689 case DELETED_PEOPLE: 1690 case DELETED_GROUPS: 1691 throw new UnsupportedOperationException(); 1692 1693 default: 1694 throw new IllegalArgumentException("Unknown URL " + uri); 1695 } 1696 1697 // Perform the query and set the notification uri 1698 final Cursor c = qb.query(db, projection, selection, selectionArgs, 1699 groupBy, null, sortOrder, limit); 1700 if (c != null) { 1701 c.setNotificationUri(mContext.getContentResolver(), 1702 android.provider.Contacts.CONTENT_URI); 1703 } 1704 return c; 1705 } 1706 1707 private void applyRawContactsAccount(SQLiteQueryBuilder qb) { 1708 StringBuilder sb = new StringBuilder(); 1709 appendRawContactsAccount(sb); 1710 qb.appendWhere(sb.toString()); 1711 } 1712 1713 private void appendRawContactsAccount(StringBuilder sb) { 1714 sb.append(RawContacts.ACCOUNT_NAME + "="); 1715 DatabaseUtils.appendEscapedSQLString(sb, mAccount.name); 1716 sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "="); 1717 DatabaseUtils.appendEscapedSQLString(sb, mAccount.type); 1718 } 1719 1720 private void applyGroupAccount(SQLiteQueryBuilder qb) { 1721 StringBuilder sb = new StringBuilder(); 1722 appendGroupAccount(sb); 1723 qb.appendWhere(sb.toString()); 1724 } 1725 1726 private void appendGroupAccount(StringBuilder sb) { 1727 sb.append(Groups.ACCOUNT_NAME + "="); 1728 DatabaseUtils.appendEscapedSQLString(sb, mAccount.name); 1729 sb.append(" AND " + Groups.ACCOUNT_TYPE + "="); 1730 DatabaseUtils.appendEscapedSQLString(sb, mAccount.type); 1731 } 1732 1733 /** 1734 * Build a WHERE clause that restricts the query to match people that are a member of 1735 * a group with a particular name. The projection map of the query must include 1736 * {@link People#_ID}. 1737 * 1738 * @param groupName The name of the group 1739 * @return The where clause. 1740 */ 1741 private String buildGroupNameMatchWhereClause(String groupName) { 1742 return "people._id IN " 1743 + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID 1744 + " FROM " + Tables.DATA_JOIN_MIMETYPES 1745 + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE 1746 + "' AND " + GroupMembership.GROUP_ROW_ID + "=" 1747 + "(SELECT " + Tables.GROUPS + "." + Groups._ID 1748 + " FROM " + Tables.GROUPS 1749 + " WHERE " + Groups.TITLE + "=" 1750 + DatabaseUtils.sqlEscapeString(groupName) + "))"; 1751 } 1752 1753 /** 1754 * Called when a change has been made. 1755 * 1756 * @param uri the uri that the change was made to 1757 */ 1758 private void onChange(Uri uri) { 1759 mContext.getContentResolver().notifyChange(android.provider.Contacts.CONTENT_URI, null); 1760 } 1761 1762 public String getType(Uri uri) { 1763 int match = sUriMatcher.match(uri); 1764 switch (match) { 1765 case EXTENSIONS: 1766 case PEOPLE_EXTENSIONS: 1767 return Extensions.CONTENT_TYPE; 1768 case EXTENSIONS_ID: 1769 case PEOPLE_EXTENSIONS_ID: 1770 return Extensions.CONTENT_ITEM_TYPE; 1771 case PEOPLE: 1772 return "vnd.android.cursor.dir/person"; 1773 case PEOPLE_ID: 1774 return "vnd.android.cursor.item/person"; 1775 case PEOPLE_PHONES: 1776 return "vnd.android.cursor.dir/phone"; 1777 case PEOPLE_PHONES_ID: 1778 return "vnd.android.cursor.item/phone"; 1779 case PEOPLE_CONTACTMETHODS: 1780 return "vnd.android.cursor.dir/contact-methods"; 1781 case PEOPLE_CONTACTMETHODS_ID: 1782 return getContactMethodType(uri); 1783 case PHONES: 1784 return "vnd.android.cursor.dir/phone"; 1785 case PHONES_ID: 1786 return "vnd.android.cursor.item/phone"; 1787 case PHONES_FILTER: 1788 return "vnd.android.cursor.dir/phone"; 1789 case PHOTOS_ID: 1790 return "vnd.android.cursor.item/photo"; 1791 case PHOTOS: 1792 return "vnd.android.cursor.dir/photo"; 1793 case PEOPLE_PHOTO: 1794 return "vnd.android.cursor.item/photo"; 1795 case CONTACTMETHODS: 1796 return "vnd.android.cursor.dir/contact-methods"; 1797 case CONTACTMETHODS_ID: 1798 return getContactMethodType(uri); 1799 case ORGANIZATIONS: 1800 return "vnd.android.cursor.dir/organizations"; 1801 case ORGANIZATIONS_ID: 1802 return "vnd.android.cursor.item/organization"; 1803 case SEARCH_SUGGESTIONS: 1804 return SearchManager.SUGGEST_MIME_TYPE; 1805 case SEARCH_SHORTCUT: 1806 return SearchManager.SHORTCUT_MIME_TYPE; 1807 default: 1808 throw new IllegalArgumentException("Unknown URI"); 1809 } 1810 } 1811 1812 private String getContactMethodType(Uri url) { 1813 String mime = null; 1814 1815 Cursor c = query(url, new String[] {ContactMethods.KIND}, null, null, null, null); 1816 if (c != null) { 1817 try { 1818 if (c.moveToFirst()) { 1819 int kind = c.getInt(0); 1820 switch (kind) { 1821 case android.provider.Contacts.KIND_EMAIL: 1822 mime = "vnd.android.cursor.item/email"; 1823 break; 1824 1825 case android.provider.Contacts.KIND_IM: 1826 mime = "vnd.android.cursor.item/jabber-im"; 1827 break; 1828 1829 case android.provider.Contacts.KIND_POSTAL: 1830 mime = "vnd.android.cursor.item/postal-address"; 1831 break; 1832 } 1833 } 1834 } finally { 1835 c.close(); 1836 } 1837 } 1838 return mime; 1839 } 1840} 1841