LegacyApiSupport.java revision 33b41fdb8d7c3c654cb070799c9d6e2b4ab16078
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.RawContactsColumns; 19import com.android.providers.contacts.OpenHelper.DataColumns; 20import com.android.providers.contacts.OpenHelper.ExtensionsColumns; 21import com.android.providers.contacts.OpenHelper.GroupsColumns; 22import com.android.providers.contacts.OpenHelper.GroupMembershipColumns; 23import com.android.providers.contacts.OpenHelper.MimetypesColumns; 24import com.android.providers.contacts.OpenHelper.PhoneColumns; 25import com.android.providers.contacts.OpenHelper.Tables; 26 27import android.content.ContentUris; 28import android.content.ContentValues; 29import android.content.Context; 30import android.content.UriMatcher; 31import android.database.Cursor; 32import android.database.DatabaseUtils; 33import android.database.sqlite.SQLiteDatabase; 34import android.database.sqlite.SQLiteQueryBuilder; 35import android.database.sqlite.SQLiteStatement; 36import android.net.Uri; 37import android.provider.BaseColumns; 38import android.provider.ContactsContract; 39import android.provider.Contacts.ContactMethods; 40import android.provider.Contacts.People; 41import android.provider.ContactsContract.CommonDataKinds; 42import android.provider.ContactsContract.RawContacts; 43import android.provider.ContactsContract.Data; 44import android.provider.ContactsContract.Groups; 45import android.provider.ContactsContract.Presence; 46import android.provider.ContactsContract.CommonDataKinds.Email; 47import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 48import android.provider.ContactsContract.CommonDataKinds.Im; 49import android.provider.ContactsContract.CommonDataKinds.Note; 50import android.provider.ContactsContract.CommonDataKinds.Organization; 51import android.provider.ContactsContract.CommonDataKinds.Phone; 52import android.provider.ContactsContract.CommonDataKinds.Photo; 53import android.provider.ContactsContract.CommonDataKinds.StructuredName; 54import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 55import android.text.TextUtils; 56 57import java.util.HashMap; 58 59public class LegacyApiSupport implements OpenHelper.Delegate { 60 61 private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 62 63 private static final int PEOPLE = 1; 64 private static final int PEOPLE_ID = 2; 65 private static final int PEOPLE_UPDATE_CONTACT_TIME = 3; 66 private static final int ORGANIZATIONS = 4; 67 private static final int ORGANIZATIONS_ID = 5; 68 private static final int PEOPLE_CONTACTMETHODS = 6; 69 private static final int PEOPLE_CONTACTMETHODS_ID = 7; 70 private static final int CONTACTMETHODS = 8; 71 private static final int CONTACTMETHODS_ID = 9; 72 private static final int PEOPLE_PHONES = 10; 73 private static final int PEOPLE_PHONES_ID = 11; 74 private static final int PHONES = 12; 75 private static final int PHONES_ID = 13; 76 private static final int EXTENSIONS = 14; 77 private static final int EXTENSIONS_ID = 15; 78 private static final int PEOPLE_EXTENSIONS = 16; 79 private static final int PEOPLE_EXTENSIONS_ID = 17; 80 private static final int GROUPS = 18; 81 private static final int GROUPS_ID = 19; 82 private static final int GROUPMEMBERSHIP = 20; 83 private static final int GROUPMEMBERSHIP_ID = 21; 84 private static final int PEOPLE_GROUPMEMBERSHIP = 22; 85 private static final int PEOPLE_GROUPMEMBERSHIP_ID = 23; 86 private static final int PEOPLE_PHOTO = 24; 87 private static final int PHOTOS = 25; 88 private static final int PHOTOS_ID = 26; 89 private static final int PRESENCE = 27; 90 private static final int PRESENCE_ID = 28; 91 private static final int PEOPLE_FILTER = 29; 92 private static final int DELETED_PEOPLE = 30; 93 private static final int DELETED_GROUPS = 31; 94 95 96 private static final String PEOPLE_JOINS = 97 " LEFT OUTER JOIN data name ON (contacts._id = name.contact_id" 98 + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = name.mimetype_id)" 99 + "='" + StructuredName.CONTENT_ITEM_TYPE + "')" 100 + " LEFT OUTER JOIN data organization ON (contacts._id = organization.contact_id" 101 + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = organization.mimetype_id)" 102 + "='" + Organization.CONTENT_ITEM_TYPE + "' AND organization.is_primary)" 103 + " LEFT OUTER JOIN data email ON (contacts._id = email.contact_id" 104 + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = email.mimetype_id)" 105 + "='" + Email.CONTENT_ITEM_TYPE + "' AND email.is_primary)" 106 + " LEFT OUTER JOIN data note ON (contacts._id = note.contact_id" 107 + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = note.mimetype_id)" 108 + "='" + Note.CONTENT_ITEM_TYPE + "')" 109 + " LEFT OUTER JOIN data phone ON (contacts._id = phone.contact_id" 110 + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = phone.mimetype_id)" 111 + "='" + Phone.CONTENT_ITEM_TYPE + "' AND phone.is_primary)"; 112 113 public static final String DATA_JOINS = 114 " JOIN mimetypes ON (mimetypes._id = data.mimetype_id)" 115 + " JOIN contacts ON (contacts._id = data.contact_id)" 116 + PEOPLE_JOINS; 117 118 public static final String PRESENCE_JOINS = 119 " LEFT OUTER JOIN presence ON (" 120 + " presence.presence_id = (SELECT max(presence_id) FROM presence" 121 + " WHERE view_v1_people._id = presence.contact_id))"; 122 123 private static final String PHONETIC_NAME_SQL = "trim(trim(" 124 + "ifnull(name." + StructuredName.PHONETIC_GIVEN_NAME + ",' ')||' '||" 125 + "ifnull(name." + StructuredName.PHONETIC_MIDDLE_NAME + ",' '))||' '||" 126 + "ifnull(name." + StructuredName.PHONETIC_FAMILY_NAME + ",' ')) "; 127 128 private static final String CONTACT_METHOD_KIND_SQL = 129 "CAST ((CASE WHEN mimetype='" + Email.CONTENT_ITEM_TYPE + "'" 130 + " THEN " + android.provider.Contacts.KIND_EMAIL 131 + " ELSE" 132 + " (CASE WHEN mimetype='" + Im.CONTENT_ITEM_TYPE +"'" 133 + " THEN " + android.provider.Contacts.KIND_IM 134 + " ELSE" 135 + " (CASE WHEN mimetype='" + StructuredPostal.CONTENT_ITEM_TYPE + "'" 136 + " THEN " + android.provider.Contacts.KIND_POSTAL 137 + " ELSE" 138 + " NULL" 139 + " END)" 140 + " END)" 141 + " END) AS INTEGER)"; 142 143 public interface LegacyTables { 144 public static final String PEOPLE = "view_v1_people"; 145 public static final String PEOPLE_JOIN_PRESENCE = "view_v1_people" + PRESENCE_JOINS; 146 public static final String GROUPS = "view_v1_groups"; 147 public static final String ORGANIZATIONS = "view_v1_organizations"; 148 public static final String CONTACT_METHODS = "view_v1_contact_methods"; 149 public static final String PHONES = "view_v1_phones"; 150 public static final String EXTENSIONS = "view_v1_extensions"; 151 public static final String GROUP_MEMBERSHIP = "view_v1_group_membership"; 152 public static final String PHOTOS = "view_v1_photos"; 153 public static final String PRESENCE_JOIN_CONTACTS = Tables.PRESENCE + 154 " LEFT OUTER JOIN " + Tables.CONTACTS 155 + " ON (" + Tables.PRESENCE + "." + Presence.CONTACT_ID + "=" 156 + RawContactsColumns.CONCRETE_ID + ")"; 157 } 158 159 private static final String[] ORGANIZATION_MIME_TYPES = new String[] { 160 Organization.CONTENT_ITEM_TYPE 161 }; 162 163 private static final String[] CONTACT_METHOD_MIME_TYPES = new String[] { 164 Email.CONTENT_ITEM_TYPE, 165 Im.CONTENT_ITEM_TYPE, 166 StructuredPostal.CONTENT_ITEM_TYPE, 167 }; 168 169 private static final String[] PHONE_MIME_TYPES = new String[] { 170 Phone.CONTENT_ITEM_TYPE 171 }; 172 173 private interface PhotoQuery { 174 String[] COLUMNS = { Data._ID }; 175 176 int _ID = 0; 177 } 178 179 /** 180 * A custom data row that is used to store legacy photo data fields no 181 * longer directly supported by the API. 182 */ 183 private interface LegacyPhotoData { 184 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo_v1_extras"; 185 186 public static final String PHOTO_DATA_ID = Data.DATA1; 187 public static final String LOCAL_VERSION = Data.DATA2; 188 public static final String DOWNLOAD_REQUIRED = Data.DATA3; 189 public static final String EXISTS_ON_SERVER = Data.DATA4; 190 public static final String SYNC_ERROR = Data.DATA5; 191 } 192 193 public static final String LEGACY_PHOTO_JOIN = 194 " LEFT OUTER JOIN data legacy_photo ON (contacts._id = legacy_photo.contact_id" 195 + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = legacy_photo.mimetype_id)" 196 + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'" 197 + " AND " + DataColumns.CONCRETE_ID + " = legacy_photo." + LegacyPhotoData.PHOTO_DATA_ID 198 + ")"; 199 200 private static final HashMap<String, String> sPeopleProjectionMap; 201 private static final HashMap<String, String> sOrganizationProjectionMap; 202 private static final HashMap<String, String> sContactMethodProjectionMap; 203 private static final HashMap<String, String> sPhoneProjectionMap; 204 private static final HashMap<String, String> sExtensionProjectionMap; 205 private static final HashMap<String, String> sGroupProjectionMap; 206 private static final HashMap<String, String> sGroupMembershipProjectionMap; 207 private static final HashMap<String, String> sPhotoProjectionMap; 208 private static final HashMap<String, String> sPresenceProjectionMap; 209 210 static { 211 212 // Contacts URI matching table 213 UriMatcher matcher = sUriMatcher; 214 215 String authority = android.provider.Contacts.AUTHORITY; 216 matcher.addURI(authority, "extensions", EXTENSIONS); 217 matcher.addURI(authority, "extensions/#", EXTENSIONS_ID); 218 matcher.addURI(authority, "groups", GROUPS); 219 matcher.addURI(authority, "groups/#", GROUPS_ID); 220// matcher.addURI(authority, "groups/name/*/members", GROUP_NAME_MEMBERS); 221// matcher.addURI(authority, "groups/name/*/members/filter/*", 222// GROUP_NAME_MEMBERS_FILTER); 223// matcher.addURI(authority, "groups/system_id/*/members", GROUP_SYSTEM_ID_MEMBERS); 224// matcher.addURI(authority, "groups/system_id/*/members/filter/*", 225// GROUP_SYSTEM_ID_MEMBERS_FILTER); 226 matcher.addURI(authority, "groupmembership", GROUPMEMBERSHIP); 227 matcher.addURI(authority, "groupmembership/#", GROUPMEMBERSHIP_ID); 228// matcher.addURI(authority, "groupmembershipraw", GROUPMEMBERSHIP_RAW); 229 matcher.addURI(authority, "people", PEOPLE); 230// matcher.addURI(authority, "people/strequent", PEOPLE_STREQUENT); 231// matcher.addURI(authority, "people/strequent/filter/*", PEOPLE_STREQUENT_FILTER); 232 matcher.addURI(authority, "people/filter/*", PEOPLE_FILTER); 233// matcher.addURI(authority, "people/with_phones_filter/*", 234// PEOPLE_WITH_PHONES_FILTER); 235// matcher.addURI(authority, "people/with_email_or_im_filter/*", 236// PEOPLE_WITH_EMAIL_OR_IM_FILTER); 237 matcher.addURI(authority, "people/#", PEOPLE_ID); 238 matcher.addURI(authority, "people/#/extensions", PEOPLE_EXTENSIONS); 239 matcher.addURI(authority, "people/#/extensions/#", PEOPLE_EXTENSIONS_ID); 240 matcher.addURI(authority, "people/#/phones", PEOPLE_PHONES); 241 matcher.addURI(authority, "people/#/phones/#", PEOPLE_PHONES_ID); 242// matcher.addURI(authority, "people/#/phones_with_presence", 243// PEOPLE_PHONES_WITH_PRESENCE); 244 matcher.addURI(authority, "people/#/photo", PEOPLE_PHOTO); 245// matcher.addURI(authority, "people/#/photo/data", PEOPLE_PHOTO_DATA); 246 matcher.addURI(authority, "people/#/contact_methods", PEOPLE_CONTACTMETHODS); 247// matcher.addURI(authority, "people/#/contact_methods_with_presence", 248// PEOPLE_CONTACTMETHODS_WITH_PRESENCE); 249 matcher.addURI(authority, "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID); 250// matcher.addURI(authority, "people/#/organizations", PEOPLE_ORGANIZATIONS); 251// matcher.addURI(authority, "people/#/organizations/#", PEOPLE_ORGANIZATIONS_ID); 252 matcher.addURI(authority, "people/#/groupmembership", PEOPLE_GROUPMEMBERSHIP); 253 matcher.addURI(authority, "people/#/groupmembership/#", PEOPLE_GROUPMEMBERSHIP_ID); 254// matcher.addURI(authority, "people/raw", PEOPLE_RAW); 255// matcher.addURI(authority, "people/owner", PEOPLE_OWNER); 256 matcher.addURI(authority, "people/#/update_contact_time", 257 PEOPLE_UPDATE_CONTACT_TIME); 258 matcher.addURI(authority, "deleted_people", DELETED_PEOPLE); 259 matcher.addURI(authority, "deleted_groups", DELETED_GROUPS); 260 matcher.addURI(authority, "phones", PHONES); 261// matcher.addURI(authority, "phones_with_presence", PHONES_WITH_PRESENCE); 262// matcher.addURI(authority, "phones/filter/*", PHONES_FILTER); 263// matcher.addURI(authority, "phones/filter_name/*", PHONES_FILTER_NAME); 264// matcher.addURI(authority, "phones/mobile_filter_name/*", 265// PHONES_MOBILE_FILTER_NAME); 266 matcher.addURI(authority, "phones/#", PHONES_ID); 267 matcher.addURI(authority, "photos", PHOTOS); 268 matcher.addURI(authority, "photos/#", PHOTOS_ID); 269 matcher.addURI(authority, "contact_methods", CONTACTMETHODS); 270// matcher.addURI(authority, "contact_methods/email", CONTACTMETHODS_EMAIL); 271// matcher.addURI(authority, "contact_methods/email/*", CONTACTMETHODS_EMAIL_FILTER); 272 matcher.addURI(authority, "contact_methods/#", CONTACTMETHODS_ID); 273// matcher.addURI(authority, "contact_methods/with_presence", 274// CONTACTMETHODS_WITH_PRESENCE); 275 matcher.addURI(authority, "presence", PRESENCE); 276 matcher.addURI(authority, "presence/#", PRESENCE_ID); 277 matcher.addURI(authority, "organizations", ORGANIZATIONS); 278 matcher.addURI(authority, "organizations/#", ORGANIZATIONS_ID); 279// matcher.addURI(authority, "voice_dialer_timestamp", VOICE_DIALER_TIMESTAMP); 280// matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY, 281// SEARCH_SUGGESTIONS); 282// matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", 283// SEARCH_SUGGESTIONS); 284// matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/#", 285// SEARCH_SHORTCUT); 286// matcher.addURI(authority, "settings", SETTINGS); 287// 288// matcher.addURI(authority, "live_folders/people", LIVE_FOLDERS_PEOPLE); 289// matcher.addURI(authority, "live_folders/people/*", 290// LIVE_FOLDERS_PEOPLE_GROUP_NAME); 291// matcher.addURI(authority, "live_folders/people_with_phones", 292// LIVE_FOLDERS_PEOPLE_WITH_PHONES); 293// matcher.addURI(authority, "live_folders/favorites", 294// LIVE_FOLDERS_PEOPLE_FAVORITES); 295// 296// // Call log URI matching table 297// matcher.addURI(CALL_LOG_AUTHORITY, "calls", CALLS); 298// matcher.addURI(CALL_LOG_AUTHORITY, "calls/filter/*", CALLS_FILTER); 299// matcher.addURI(CALL_LOG_AUTHORITY, "calls/#", CALLS_ID); 300 301 302 HashMap<String, String> peopleProjectionMap = new HashMap<String, String>(); 303 peopleProjectionMap.put(People.NAME, People.NAME); 304 peopleProjectionMap.put(People.DISPLAY_NAME, People.DISPLAY_NAME); 305 peopleProjectionMap.put(People.PHONETIC_NAME, People.PHONETIC_NAME); 306 peopleProjectionMap.put(People.NOTES, People.NOTES); 307 peopleProjectionMap.put(People.TIMES_CONTACTED, People.TIMES_CONTACTED); 308 peopleProjectionMap.put(People.LAST_TIME_CONTACTED, People.LAST_TIME_CONTACTED); 309 peopleProjectionMap.put(People.CUSTOM_RINGTONE, People.CUSTOM_RINGTONE); 310 peopleProjectionMap.put(People.SEND_TO_VOICEMAIL, People.SEND_TO_VOICEMAIL); 311 peopleProjectionMap.put(People.STARRED, People.STARRED); 312 313 sPeopleProjectionMap = new HashMap<String, String>(peopleProjectionMap); 314 sPeopleProjectionMap.put(People.PRIMARY_ORGANIZATION_ID, People.PRIMARY_ORGANIZATION_ID); 315 sPeopleProjectionMap.put(People.PRIMARY_EMAIL_ID, People.PRIMARY_EMAIL_ID); 316 sPeopleProjectionMap.put(People.PRIMARY_PHONE_ID, People.PRIMARY_PHONE_ID); 317 sPeopleProjectionMap.put(People.NUMBER, People.NUMBER); 318 sPeopleProjectionMap.put(People.TYPE, People.TYPE); 319 sPeopleProjectionMap.put(People.LABEL, People.LABEL); 320 sPeopleProjectionMap.put(People.NUMBER_KEY, People.NUMBER_KEY); 321 sPeopleProjectionMap.put(People.IM_PROTOCOL, People.IM_PROTOCOL); 322 sPeopleProjectionMap.put(People.IM_HANDLE, People.IM_HANDLE); 323 sPeopleProjectionMap.put(People.IM_ACCOUNT, People.IM_ACCOUNT); 324 sPeopleProjectionMap.put(People.PRESENCE_STATUS, People.PRESENCE_STATUS); 325 sPeopleProjectionMap.put(People.PRESENCE_CUSTOM_STATUS, People.PRESENCE_CUSTOM_STATUS); 326 327 sOrganizationProjectionMap = new HashMap<String, String>(); 328 sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.PERSON_ID, 329 android.provider.Contacts.Organizations.PERSON_ID); 330 sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.ISPRIMARY, 331 android.provider.Contacts.Organizations.ISPRIMARY); 332 sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.COMPANY, 333 android.provider.Contacts.Organizations.COMPANY); 334 sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TYPE, 335 android.provider.Contacts.Organizations.TYPE); 336 sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.LABEL, 337 android.provider.Contacts.Organizations.LABEL); 338 sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TITLE, 339 android.provider.Contacts.Organizations.TITLE); 340 341 sContactMethodProjectionMap = new HashMap<String, String>(peopleProjectionMap); 342 sContactMethodProjectionMap.put(ContactMethods.PERSON_ID, ContactMethods.PERSON_ID); 343 sContactMethodProjectionMap.put(ContactMethods.KIND, ContactMethods.KIND); 344 sContactMethodProjectionMap.put(ContactMethods.ISPRIMARY, ContactMethods.ISPRIMARY); 345 sContactMethodProjectionMap.put(ContactMethods.TYPE, ContactMethods.TYPE); 346 sContactMethodProjectionMap.put(ContactMethods.DATA, ContactMethods.DATA); 347 sContactMethodProjectionMap.put(ContactMethods.LABEL, ContactMethods.LABEL); 348 sContactMethodProjectionMap.put(ContactMethods.AUX_DATA, ContactMethods.AUX_DATA); 349 350 sPhoneProjectionMap = new HashMap<String, String>(peopleProjectionMap); 351 sPhoneProjectionMap.put(android.provider.Contacts.Phones.PERSON_ID, 352 android.provider.Contacts.Phones.PERSON_ID); 353 sPhoneProjectionMap.put(android.provider.Contacts.Phones.ISPRIMARY, 354 android.provider.Contacts.Phones.ISPRIMARY); 355 sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER, 356 android.provider.Contacts.Phones.NUMBER); 357 sPhoneProjectionMap.put(android.provider.Contacts.Phones.TYPE, 358 android.provider.Contacts.Phones.TYPE); 359 sPhoneProjectionMap.put(android.provider.Contacts.Phones.LABEL, 360 android.provider.Contacts.Phones.LABEL); 361 sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER_KEY, 362 android.provider.Contacts.Phones.NUMBER_KEY); 363 364 sExtensionProjectionMap = new HashMap<String, String>(); 365 sExtensionProjectionMap.put(android.provider.Contacts.Extensions.PERSON_ID, 366 android.provider.Contacts.Extensions.PERSON_ID); 367 sExtensionProjectionMap.put(android.provider.Contacts.Extensions.NAME, 368 android.provider.Contacts.Extensions.NAME); 369 sExtensionProjectionMap.put(android.provider.Contacts.Extensions.VALUE, 370 android.provider.Contacts.Extensions.VALUE); 371 372 sGroupProjectionMap = new HashMap<String, String>(); 373 sGroupProjectionMap.put(android.provider.Contacts.Groups._ID, 374 android.provider.Contacts.Groups._ID); 375 sGroupProjectionMap.put(android.provider.Contacts.Groups.NAME, 376 android.provider.Contacts.Groups.NAME); 377 sGroupProjectionMap.put(android.provider.Contacts.Groups.NOTES, 378 android.provider.Contacts.Groups.NOTES); 379 sGroupProjectionMap.put(android.provider.Contacts.Groups.SYSTEM_ID, 380 android.provider.Contacts.Groups.SYSTEM_ID); 381 382 sGroupMembershipProjectionMap = new HashMap<String, String>(); 383 sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.PERSON_ID, 384 android.provider.Contacts.GroupMembership.PERSON_ID); 385 sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.GROUP_ID, 386 android.provider.Contacts.GroupMembership.GROUP_ID); 387 388 sPhotoProjectionMap = new HashMap<String, String>(); 389 sPhotoProjectionMap.put(android.provider.Contacts.Photos.PERSON_ID, 390 android.provider.Contacts.Photos.PERSON_ID); 391 sPhotoProjectionMap.put(android.provider.Contacts.Photos.DATA, 392 android.provider.Contacts.Photos.DATA); 393 sPhotoProjectionMap.put(android.provider.Contacts.Photos.LOCAL_VERSION, 394 android.provider.Contacts.Photos.LOCAL_VERSION); 395 sPhotoProjectionMap.put(android.provider.Contacts.Photos.DOWNLOAD_REQUIRED, 396 android.provider.Contacts.Photos.DOWNLOAD_REQUIRED); 397 sPhotoProjectionMap.put(android.provider.Contacts.Photos.EXISTS_ON_SERVER, 398 android.provider.Contacts.Photos.EXISTS_ON_SERVER); 399 sPhotoProjectionMap.put(android.provider.Contacts.Photos.SYNC_ERROR, 400 android.provider.Contacts.Photos.SYNC_ERROR); 401 402 sPresenceProjectionMap = new HashMap<String, String>(); 403 sPresenceProjectionMap.put(android.provider.Contacts.Presence._ID, 404 Tables.PRESENCE + "." + Presence._ID 405 + " AS " + android.provider.Contacts.Presence._ID); 406 sPresenceProjectionMap.put(android.provider.Contacts.Presence.PERSON_ID, 407 Tables.PRESENCE + "." + Presence.CONTACT_ID 408 + " AS " + android.provider.Contacts.Presence.PERSON_ID); 409 sPresenceProjectionMap.put(android.provider.Contacts.Presence.IM_PROTOCOL, 410 Presence.IM_PROTOCOL 411 + " AS " + android.provider.Contacts.Presence.IM_PROTOCOL); 412 sPresenceProjectionMap.put(android.provider.Contacts.Presence.IM_HANDLE, 413 Presence.IM_HANDLE 414 + " AS " + android.provider.Contacts.Presence.IM_HANDLE); 415 sPresenceProjectionMap.put(android.provider.Contacts.Presence.IM_ACCOUNT, 416 Presence.IM_ACCOUNT 417 + " AS " + android.provider.Contacts.Presence.IM_ACCOUNT); 418 sPresenceProjectionMap.put(android.provider.Contacts.Presence.PRESENCE_STATUS, 419 Presence.PRESENCE_STATUS 420 + " AS " + android.provider.Contacts.Presence.PRESENCE_STATUS); 421 sPresenceProjectionMap.put(android.provider.Contacts.Presence.PRESENCE_CUSTOM_STATUS, 422 Presence.PRESENCE_CUSTOM_STATUS 423 + " AS " + android.provider.Contacts.Presence.PRESENCE_CUSTOM_STATUS); 424 } 425 426 private final Context mContext; 427 private final OpenHelper mOpenHelper; 428 private final ContactsProvider2 mContactsProvider; 429 private final NameSplitter mPhoneticNameSplitter; 430 431 /** Precompiled sql statement for incrementing times contacted for an aggregate */ 432 private final SQLiteStatement mLastTimeContactedUpdate; 433 434 private final ContentValues mValues = new ContentValues(); 435 436 public LegacyApiSupport(Context context, OpenHelper openHelper, 437 ContactsProvider2 contactsProvider) { 438 mContext = context; 439 mContactsProvider = contactsProvider; 440 mOpenHelper = openHelper; 441 mOpenHelper.setDelegate(this); 442 443 mPhoneticNameSplitter = new NameSplitter("", "", "", 444 context.getString(com.android.internal.R.string.common_name_conjunctions)); 445 446 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 447 mLastTimeContactedUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET " 448 + RawContacts.TIMES_CONTACTED + "=" 449 + RawContacts.TIMES_CONTACTED + "+1," 450 + RawContacts.LAST_TIME_CONTACTED + "=? WHERE " 451 + RawContacts._ID + "=?"); 452 } 453 454 455 public void createDatabase(SQLiteDatabase db) { 456 457 db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PEOPLE + ";"); 458 db.execSQL("CREATE VIEW " + LegacyTables.PEOPLE + " AS SELECT " + 459 RawContactsColumns.CONCRETE_ID 460 + " AS " + android.provider.Contacts.People._ID + ", " + 461 "name." + StructuredName.DISPLAY_NAME 462 + " AS " + People.NAME + ", " + 463 Tables.CONTACTS + "." + RawContactsColumns.DISPLAY_NAME 464 + " AS " + People.DISPLAY_NAME + ", " + 465 PHONETIC_NAME_SQL 466 + " AS " + People.PHONETIC_NAME + " , " + 467 "note." + Note.NOTE 468 + " AS " + People.NOTES + ", " + 469 Tables.CONTACTS + "." + RawContacts.TIMES_CONTACTED 470 + " AS " + People.TIMES_CONTACTED + ", " + 471 Tables.CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED 472 + " AS " + People.LAST_TIME_CONTACTED + ", " + 473 Tables.CONTACTS + "." + RawContacts.CUSTOM_RINGTONE 474 + " AS " + People.CUSTOM_RINGTONE + ", " + 475 Tables.CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL 476 + " AS " + People.SEND_TO_VOICEMAIL + ", " + 477 Tables.CONTACTS + "." + RawContacts.STARRED 478 + " AS " + People.STARRED + ", " + 479 "organization." + Data._ID 480 + " AS " + People.PRIMARY_ORGANIZATION_ID + ", " + 481 "email." + Data._ID 482 + " AS " + People.PRIMARY_EMAIL_ID + ", " + 483 "phone." + Data._ID 484 + " AS " + People.PRIMARY_PHONE_ID + ", " + 485 "phone." + Phone.NUMBER 486 + " AS " + People.NUMBER + ", " + 487 "phone." + Phone.TYPE 488 + " AS " + People.TYPE + ", " + 489 "phone." + Phone.LABEL 490 + " AS " + People.LABEL + ", " + 491 "phone." + PhoneColumns.NORMALIZED_NUMBER 492 + " AS " + People.NUMBER_KEY + ", " + 493 RawContacts.IS_RESTRICTED + 494 " FROM " + Tables.CONTACTS + PEOPLE_JOINS + 495 " WHERE " + Tables.CONTACTS + "." + RawContacts.DELETED + "=0" + 496 ";"); 497 498 db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.ORGANIZATIONS + ";"); 499 db.execSQL("CREATE VIEW " + LegacyTables.ORGANIZATIONS + " AS SELECT " + 500 DataColumns.CONCRETE_ID 501 + " AS " + android.provider.Contacts.Organizations._ID + ", " + 502 Data.CONTACT_ID 503 + " AS " + android.provider.Contacts.Organizations.PERSON_ID + ", " + 504 Data.IS_PRIMARY 505 + " AS " + android.provider.Contacts.Organizations.ISPRIMARY + ", " + 506 Organization.COMPANY 507 + " AS " + android.provider.Contacts.Organizations.COMPANY + ", " + 508 Organization.TYPE 509 + " AS " + android.provider.Contacts.Organizations.TYPE + ", " + 510 Organization.LABEL 511 + " AS " + android.provider.Contacts.Organizations.LABEL + ", " + 512 Organization.TITLE 513 + " AS " + android.provider.Contacts.Organizations.TITLE + ", " + 514 RawContacts.IS_RESTRICTED + 515 " FROM " + Tables.DATA_JOIN_MIMETYPE_CONTACTS + 516 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='" 517 + Organization.CONTENT_ITEM_TYPE + "'" 518 + " AND " + Tables.CONTACTS + "." + RawContacts.DELETED + "=0" + 519 ";"); 520 521 db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.CONTACT_METHODS + ";"); 522 db.execSQL("CREATE VIEW " + LegacyTables.CONTACT_METHODS + " AS SELECT " + 523 DataColumns.CONCRETE_ID 524 + " AS " + ContactMethods._ID + ", " + 525 DataColumns.CONCRETE_CONTACT_ID 526 + " AS " + ContactMethods.PERSON_ID + ", " + 527 CONTACT_METHOD_KIND_SQL 528 + " AS " + ContactMethods.KIND + ", " + 529 DataColumns.CONCRETE_IS_PRIMARY 530 + " AS " + ContactMethods.ISPRIMARY + ", " + 531 DataColumns.CONCRETE_DATA1 532 + " AS " + ContactMethods.TYPE + ", " + 533 DataColumns.CONCRETE_DATA2 534 + " AS " + ContactMethods.DATA + ", " + 535 DataColumns.CONCRETE_DATA3 536 + " AS " + ContactMethods.LABEL + ", " + 537 DataColumns.CONCRETE_DATA14 538 + " AS " + ContactMethods.AUX_DATA + ", " + 539 "name." + StructuredName.DISPLAY_NAME 540 + " AS " + ContactMethods.NAME + ", " + 541 Tables.CONTACTS + "." + RawContactsColumns.DISPLAY_NAME 542 + " AS " + ContactMethods.DISPLAY_NAME + ", " + 543 PHONETIC_NAME_SQL 544 + " AS " + ContactMethods.PHONETIC_NAME + " , " + 545 "note." + Note.NOTE 546 + " AS " + ContactMethods.NOTES + ", " + 547 Tables.CONTACTS + "." + RawContacts.TIMES_CONTACTED 548 + " AS " + ContactMethods.TIMES_CONTACTED + ", " + 549 Tables.CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED 550 + " AS " + ContactMethods.LAST_TIME_CONTACTED + ", " + 551 Tables.CONTACTS + "." + RawContacts.CUSTOM_RINGTONE 552 + " AS " + ContactMethods.CUSTOM_RINGTONE + ", " + 553 Tables.CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL 554 + " AS " + ContactMethods.SEND_TO_VOICEMAIL + ", " + 555 Tables.CONTACTS + "." + RawContacts.STARRED 556 + " AS " + ContactMethods.STARRED + ", " + 557 RawContacts.IS_RESTRICTED + 558 " FROM " + Tables.DATA + DATA_JOINS + 559 " WHERE " + ContactMethods.KIND + " IS NOT NULL" 560 + " AND " + Tables.CONTACTS + "." + RawContacts.DELETED + "=0" + 561 ";"); 562 563 564 db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHONES + ";"); 565 db.execSQL("CREATE VIEW " + LegacyTables.PHONES + " AS SELECT " + 566 DataColumns.CONCRETE_ID 567 + " AS " + android.provider.Contacts.Phones._ID + ", " + 568 DataColumns.CONCRETE_CONTACT_ID 569 + " AS " + android.provider.Contacts.Phones.PERSON_ID + ", " + 570 DataColumns.CONCRETE_IS_PRIMARY 571 + " AS " + android.provider.Contacts.Phones.ISPRIMARY + ", " + 572 Tables.DATA + "." + Phone.NUMBER 573 + " AS " + android.provider.Contacts.Phones.NUMBER + ", " + 574 Tables.DATA + "." + Phone.TYPE 575 + " AS " + android.provider.Contacts.Phones.TYPE + ", " + 576 Tables.DATA + "." + Phone.LABEL 577 + " AS " + android.provider.Contacts.Phones.LABEL + ", " + 578 PhoneColumns.CONCRETE_NORMALIZED_NUMBER 579 + " AS " + android.provider.Contacts.Phones.NUMBER_KEY + ", " + 580 "name." + StructuredName.DISPLAY_NAME 581 + " AS " + android.provider.Contacts.Phones.NAME + ", " + 582 Tables.CONTACTS + "." + RawContactsColumns.DISPLAY_NAME 583 + " AS " + android.provider.Contacts.Phones.DISPLAY_NAME + ", " + 584 PHONETIC_NAME_SQL 585 + " AS " + android.provider.Contacts.Phones.PHONETIC_NAME + " , " + 586 "note." + Note.NOTE 587 + " AS " + android.provider.Contacts.Phones.NOTES + ", " + 588 Tables.CONTACTS + "." + RawContacts.TIMES_CONTACTED 589 + " AS " + android.provider.Contacts.Phones.TIMES_CONTACTED + ", " + 590 Tables.CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED 591 + " AS " + android.provider.Contacts.Phones.LAST_TIME_CONTACTED + ", " + 592 Tables.CONTACTS + "." + RawContacts.CUSTOM_RINGTONE 593 + " AS " + android.provider.Contacts.Phones.CUSTOM_RINGTONE + ", " + 594 Tables.CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL 595 + " AS " + android.provider.Contacts.Phones.SEND_TO_VOICEMAIL + ", " + 596 Tables.CONTACTS + "." + RawContacts.STARRED 597 + " AS " + android.provider.Contacts.Phones.STARRED + ", " + 598 RawContacts.IS_RESTRICTED + 599 " FROM " + Tables.DATA + DATA_JOINS + 600 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='" 601 + Phone.CONTENT_ITEM_TYPE + "'" 602 + " AND " + Tables.CONTACTS + "." + RawContacts.DELETED + "=0" + 603 ";"); 604 605 db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.EXTENSIONS + ";"); 606 db.execSQL("CREATE VIEW " + LegacyTables.EXTENSIONS + " AS SELECT " + 607 DataColumns.CONCRETE_ID 608 + " AS " + android.provider.Contacts.Extensions._ID + ", " + 609 DataColumns.CONCRETE_CONTACT_ID 610 + " AS " + android.provider.Contacts.Extensions.PERSON_ID + ", " + 611 ExtensionsColumns.NAME 612 + " AS " + android.provider.Contacts.Extensions.NAME + ", " + 613 ExtensionsColumns.VALUE 614 + " AS " + android.provider.Contacts.Extensions.VALUE + ", " + 615 RawContacts.IS_RESTRICTED + 616 " FROM " + Tables.DATA_JOIN_MIMETYPE_CONTACTS + 617 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='" 618 + android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE + "'" 619 + " AND " + Tables.CONTACTS + "." + RawContacts.DELETED + "=0" + 620 ";"); 621 622 db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUPS + ";"); 623 db.execSQL("CREATE VIEW " + LegacyTables.GROUPS + " AS SELECT " + 624 GroupsColumns.CONCRETE_ID + " AS " + android.provider.Contacts.Groups._ID + ", " + 625 Groups.TITLE + " AS " + android.provider.Contacts.Groups.NAME + ", " + 626 Groups.NOTES + " AS " + android.provider.Contacts.Groups.NOTES + " , " + 627 Groups.SYSTEM_ID + " AS " + android.provider.Contacts.Groups.SYSTEM_ID + 628 " FROM " + Tables.GROUPS + 629 ";"); 630 631 db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUP_MEMBERSHIP + ";"); 632 db.execSQL("CREATE VIEW " + LegacyTables.GROUP_MEMBERSHIP + " AS SELECT " + 633 DataColumns.CONCRETE_ID 634 + " AS " + android.provider.Contacts.GroupMembership._ID + ", " + 635 DataColumns.CONCRETE_CONTACT_ID 636 + " AS " + android.provider.Contacts.GroupMembership.PERSON_ID + ", " + 637 GroupMembership.GROUP_ROW_ID 638 + " AS " + android.provider.Contacts.GroupMembership.GROUP_ID + ", " + 639 Groups.TITLE 640 + " AS " + android.provider.Contacts.GroupMembership.NAME + ", " + 641 Groups.NOTES 642 + " AS " + android.provider.Contacts.GroupMembership.NOTES + " , " + 643 Groups.SYSTEM_ID 644 + " AS " + android.provider.Contacts.GroupMembership.SYSTEM_ID + ", " + 645 RawContacts.IS_RESTRICTED + 646 " FROM " + Tables.DATA_JOIN_PACKAGES_MIMETYPES_CONTACTS_GROUPS + 647 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='" 648 + GroupMembership.CONTENT_ITEM_TYPE + "'" 649 + " AND " + Tables.CONTACTS + "." + RawContacts.DELETED + "=0" + 650 ";"); 651 652 db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHOTOS + ";"); 653 db.execSQL("CREATE VIEW " + LegacyTables.PHOTOS + " AS SELECT " + 654 DataColumns.CONCRETE_ID 655 + " AS " + android.provider.Contacts.Photos._ID + ", " + 656 DataColumns.CONCRETE_CONTACT_ID 657 + " AS " + android.provider.Contacts.Photos.PERSON_ID + ", " + 658 Tables.DATA + "." + Photo.PHOTO 659 + " AS " + android.provider.Contacts.Photos.DATA + ", " + 660 "legacy_photo." + LegacyPhotoData.EXISTS_ON_SERVER 661 + " AS " + android.provider.Contacts.Photos.EXISTS_ON_SERVER + ", " + 662 "legacy_photo." + LegacyPhotoData.DOWNLOAD_REQUIRED 663 + " AS " + android.provider.Contacts.Photos.DOWNLOAD_REQUIRED + ", " + 664 "legacy_photo." + LegacyPhotoData.LOCAL_VERSION 665 + " AS " + android.provider.Contacts.Photos.LOCAL_VERSION + ", " + 666 "legacy_photo." + LegacyPhotoData.SYNC_ERROR 667 + " AS " + android.provider.Contacts.Photos.SYNC_ERROR + ", " + 668 RawContacts.IS_RESTRICTED + 669 " FROM " + Tables.DATA + DATA_JOINS + LEGACY_PHOTO_JOIN + 670 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='" 671 + Photo.CONTENT_ITEM_TYPE + "'" 672 + " AND " + Tables.CONTACTS + "." + RawContacts.DELETED + "=0" + 673 ";"); 674 } 675 676 public Uri insert(Uri uri, ContentValues values) { 677 final int match = sUriMatcher.match(uri); 678 long id = 0; 679 switch (match) { 680 case PEOPLE: 681 id = insertPeople(values); 682 break; 683 684 case ORGANIZATIONS: 685 id = insertOrganization(values); 686 break; 687 688 case PEOPLE_CONTACTMETHODS: { 689 long contactId = Long.parseLong(uri.getPathSegments().get(1)); 690 id = insertContactMethod(contactId, values); 691 break; 692 } 693 694 case CONTACTMETHODS: { 695 long contactId = getRequiredValue(values, ContactMethods.PERSON_ID); 696 id = insertContactMethod(contactId, values); 697 break; 698 } 699 700 case PHONES: { 701 long contactId = getRequiredValue(values, 702 android.provider.Contacts.Phones.PERSON_ID); 703 id = insertPhone(contactId, values); 704 break; 705 } 706 707 case EXTENSIONS: { 708 long contactId = getRequiredValue(values, 709 android.provider.Contacts.Extensions.PERSON_ID); 710 id = insertExtension(contactId, values); 711 break; 712 } 713 714 case GROUPS: 715 id = insertGroup(values); 716 break; 717 718 case GROUPMEMBERSHIP: { 719 long contactId = getRequiredValue(values, 720 android.provider.Contacts.GroupMembership.PERSON_ID); 721 long groupId = getRequiredValue(values, 722 android.provider.Contacts.GroupMembership.GROUP_ID); 723 id = insertGroupMembership(contactId, groupId); 724 break; 725 } 726 727 case PRESENCE: { 728 id = insertPresence(values); 729 break; 730 } 731 732 default: 733 throw new UnsupportedOperationException("Unknown uri: " + uri); 734 } 735 736 if (id < 0) { 737 return null; 738 } 739 740 final Uri result = ContentUris.withAppendedId(uri, id); 741 onChange(result); 742 return result; 743 } 744 745 private long getRequiredValue(ContentValues values, String column) { 746 if (!values.containsKey(column)) { 747 throw new RuntimeException("Required value: " + column); 748 } 749 750 return values.getAsLong(column); 751 } 752 753 private long insertPeople(ContentValues values) { 754 mValues.clear(); 755 756 OpenHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE, 757 values, People.CUSTOM_RINGTONE); 758 OpenHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL, 759 values, People.SEND_TO_VOICEMAIL); 760 OpenHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED, 761 values, People.LAST_TIME_CONTACTED); 762 OpenHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED, 763 values, People.TIMES_CONTACTED); 764 OpenHelper.copyLongValue(mValues, RawContacts.STARRED, 765 values, People.STARRED); 766 Uri contactUri = mContactsProvider.insert(RawContacts.CONTENT_URI, mValues); 767 long contactId = ContentUris.parseId(contactUri); 768 769 if (values.containsKey(People.NAME) || values.containsKey(People.PHONETIC_NAME)) { 770 mValues.clear(); 771 mValues.put(ContactsContract.Data.CONTACT_ID, contactId); 772 mValues.put(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 773 OpenHelper.copyStringValue(mValues, StructuredName.DISPLAY_NAME, 774 values, People.NAME); 775 if (values.containsKey(People.PHONETIC_NAME)) { 776 String phoneticName = values.getAsString(People.PHONETIC_NAME); 777 NameSplitter.Name parsedName = new NameSplitter.Name(); 778 mPhoneticNameSplitter.split(parsedName, phoneticName); 779 mValues.put(StructuredName.PHONETIC_GIVEN_NAME, parsedName.getGivenNames()); 780 mValues.put(StructuredName.PHONETIC_MIDDLE_NAME, parsedName.getMiddleName()); 781 mValues.put(StructuredName.PHONETIC_FAMILY_NAME, parsedName.getFamilyName()); 782 } 783 784 mContactsProvider.insert(ContactsContract.Data.CONTENT_URI, mValues); 785 } 786 787 if (values.containsKey(People.NOTES)) { 788 mValues.clear(); 789 mValues.put(Data.CONTACT_ID, contactId); 790 mValues.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE); 791 OpenHelper.copyStringValue(mValues, Note.NOTE, values, People.NOTES); 792 mContactsProvider.insert(Data.CONTENT_URI, mValues); 793 } 794 795 // TODO instant aggregation 796 return contactId; 797 } 798 799 private long insertOrganization(ContentValues values) { 800 mValues.clear(); 801 802 OpenHelper.copyLongValue(mValues, Data.CONTACT_ID, 803 values, android.provider.Contacts.Organizations.PERSON_ID); 804 mValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); 805 806 OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY, 807 values, android.provider.Contacts.Organizations.ISPRIMARY); 808 809 OpenHelper.copyStringValue(mValues, Organization.COMPANY, 810 values, android.provider.Contacts.Organizations.COMPANY); 811 812 // TYPE values happen to remain the same between V1 and V2 - can just copy the value 813 OpenHelper.copyLongValue(mValues, Organization.TYPE, 814 values, android.provider.Contacts.Organizations.TYPE); 815 816 OpenHelper.copyStringValue(mValues, Organization.LABEL, 817 values, android.provider.Contacts.Organizations.LABEL); 818 OpenHelper.copyStringValue(mValues, Organization.TITLE, 819 values, android.provider.Contacts.Organizations.TITLE); 820 821 Uri uri = mContactsProvider.insert(Data.CONTENT_URI, mValues); 822 823 return ContentUris.parseId(uri); 824 } 825 826 private long insertPhone(long contactId, ContentValues values) { 827 mValues.clear(); 828 829 mValues.put(Data.CONTACT_ID, contactId); 830 mValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 831 832 OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY, 833 values, android.provider.Contacts.Phones.ISPRIMARY); 834 835 OpenHelper.copyStringValue(mValues, Phone.NUMBER, 836 values, android.provider.Contacts.Phones.NUMBER); 837 838 // TYPE values happen to remain the same between V1 and V2 - can just copy the value 839 OpenHelper.copyLongValue(mValues, Phone.TYPE, 840 values, android.provider.Contacts.Phones.TYPE); 841 842 OpenHelper.copyStringValue(mValues, Phone.LABEL, 843 values, android.provider.Contacts.Phones.LABEL); 844 845 Uri uri = mContactsProvider.insert(Data.CONTENT_URI, mValues); 846 847 return ContentUris.parseId(uri); 848 } 849 850 private long insertContactMethod(long contactId, ContentValues values) { 851 Integer kind = values.getAsInteger(ContactMethods.KIND); 852 if (kind == null) { 853 throw new RuntimeException("Required value: " + ContactMethods.KIND); 854 } 855 856 mValues.clear(); 857 mValues.put(Data.CONTACT_ID, contactId); 858 859 OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY, values, ContactMethods.ISPRIMARY); 860 861 switch (kind) { 862 case android.provider.Contacts.KIND_EMAIL: 863 copyCommonFields(values, Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL, 864 Email.DATA, Data.DATA14); 865 break; 866 867 case android.provider.Contacts.KIND_IM: 868 copyCommonFields(values, Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL, 869 Email.DATA, Data.DATA14); 870 break; 871 872 case android.provider.Contacts.KIND_POSTAL: 873 copyCommonFields(values, StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, 874 StructuredPostal.LABEL, StructuredPostal.FORMATTED_ADDRESS, Data.DATA14); 875 break; 876 } 877 878 Uri uri = mContactsProvider.insert(Data.CONTENT_URI, mValues); 879 return ContentUris.parseId(uri); 880 } 881 882 private void copyCommonFields(ContentValues values, String mimeType, String typeColumn, 883 String labelColumn, String dataColumn, String auxDataColumn) { 884 mValues.put(Data.MIMETYPE, mimeType); 885 OpenHelper.copyLongValue(mValues, typeColumn, values, ContactMethods.TYPE); 886 OpenHelper.copyStringValue(mValues, labelColumn, values, ContactMethods.LABEL); 887 OpenHelper.copyStringValue(mValues, dataColumn, values, ContactMethods.DATA); 888 OpenHelper.copyStringValue(mValues, auxDataColumn, values, ContactMethods.AUX_DATA); 889 } 890 891 private long insertExtension(long contactId, ContentValues values) { 892 mValues.clear(); 893 894 mValues.put(Data.CONTACT_ID, contactId); 895 mValues.put(Data.MIMETYPE, android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE); 896 897 OpenHelper.copyStringValue(mValues, ExtensionsColumns.NAME, 898 values, android.provider.Contacts.People.Extensions.NAME); 899 OpenHelper.copyStringValue(mValues, ExtensionsColumns.VALUE, 900 values, android.provider.Contacts.People.Extensions.VALUE); 901 902 Uri uri = mContactsProvider.insert(Data.CONTENT_URI, mValues); 903 return ContentUris.parseId(uri); 904 } 905 906 private long insertGroup(ContentValues values) { 907 mValues.clear(); 908 909 OpenHelper.copyStringValue(mValues, Groups.TITLE, 910 values, android.provider.Contacts.Groups.NAME); 911 OpenHelper.copyStringValue(mValues, Groups.NOTES, 912 values, android.provider.Contacts.Groups.NOTES); 913 OpenHelper.copyStringValue(mValues, Groups.SYSTEM_ID, 914 values, android.provider.Contacts.Groups.SYSTEM_ID); 915 916 Uri uri = mContactsProvider.insert(Groups.CONTENT_URI, mValues); 917 return ContentUris.parseId(uri); 918 } 919 920 private long insertGroupMembership(long contactId, long groupId) { 921 mValues.clear(); 922 923 mValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); 924 mValues.put(GroupMembership.CONTACT_ID, contactId); 925 mValues.put(GroupMembership.GROUP_ROW_ID, groupId); 926 927 Uri uri = mContactsProvider.insert(Data.CONTENT_URI, mValues); 928 return ContentUris.parseId(uri); 929 } 930 931 private long insertPresence(ContentValues values) { 932 mValues.clear(); 933 934 OpenHelper.copyLongValue(mValues, Presence._ID, 935 values, android.provider.Contacts.Presence._ID); 936 OpenHelper.copyLongValue(mValues, Presence.CONTACT_ID, 937 values, android.provider.Contacts.Presence.PERSON_ID); 938 OpenHelper.copyStringValue(mValues, Presence.IM_PROTOCOL, 939 values, android.provider.Contacts.Presence.IM_PROTOCOL); 940 OpenHelper.copyStringValue(mValues, Presence.IM_HANDLE, 941 values, android.provider.Contacts.Presence.IM_HANDLE); 942 OpenHelper.copyStringValue(mValues, Presence.IM_ACCOUNT, 943 values, android.provider.Contacts.Presence.IM_ACCOUNT); 944 OpenHelper.copyLongValue(mValues, Presence.PRESENCE_STATUS, 945 values, android.provider.Contacts.Presence.PRESENCE_STATUS); 946 OpenHelper.copyStringValue(mValues, Presence.PRESENCE_CUSTOM_STATUS, 947 values, android.provider.Contacts.Presence.PRESENCE_CUSTOM_STATUS); 948 949 return mContactsProvider.insertPresence(mValues); 950 } 951 952 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 953 final int match = sUriMatcher.match(uri); 954 int count = 0; 955 switch(match) { 956 case PEOPLE_UPDATE_CONTACT_TIME: 957 count = updateContactTime(uri, values); 958 break; 959 960 case PEOPLE_PHOTO: { 961 long contactId = Long.parseLong(uri.getPathSegments().get(1)); 962 return updatePhoto(contactId, values); 963 } 964 965 case PHOTOS: 966 // TODO 967 break; 968 969 case PHOTOS_ID: 970 // TODO 971 break; 972 973 974 default: 975 throw new UnsupportedOperationException("Unknown uri: " + uri); 976 } 977 978 if (count > 0) { 979 mContext.getContentResolver().notifyChange(uri, null); 980 } 981 return count; 982 } 983 984 985 private int updateContactTime(Uri uri, ContentValues values) { 986 987 // TODO check sanctions 988 989 long lastTimeContacted; 990 if (values.containsKey(People.LAST_TIME_CONTACTED)) { 991 lastTimeContacted = values.getAsLong(People.LAST_TIME_CONTACTED); 992 } else { 993 lastTimeContacted = System.currentTimeMillis(); 994 } 995 996 long contactId = Long.parseLong(uri.getPathSegments().get(1)); 997 long aggregateId = mOpenHelper.getAggregateId(contactId); 998 if (aggregateId != 0) { 999 mContactsProvider.updateContactTime(aggregateId, lastTimeContacted); 1000 } else { 1001 mLastTimeContactedUpdate.bindLong(1, lastTimeContacted); 1002 mLastTimeContactedUpdate.bindLong(2, contactId); 1003 mLastTimeContactedUpdate.execute(); 1004 } 1005 return 1; 1006 } 1007 1008 private int updatePhoto(long contactId, ContentValues values) { 1009 1010 // TODO check sanctions 1011 1012 int count; 1013 1014 long dataId = -1; 1015 Cursor c = mContactsProvider.query(Data.CONTENT_URI, PhotoQuery.COLUMNS, 1016 Data.CONTACT_ID + "=" + contactId + " AND " 1017 + Data.MIMETYPE + "=" + mOpenHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE), 1018 null, null); 1019 try { 1020 if (c.moveToFirst()) { 1021 dataId = c.getLong(PhotoQuery._ID); 1022 } 1023 } finally { 1024 c.close(); 1025 } 1026 1027 mValues.clear(); 1028 byte[] bytes = values.getAsByteArray(android.provider.Contacts.Photos.DATA); 1029 mValues.put(Photo.PHOTO, bytes); 1030 1031 if (dataId == -1) { 1032 mValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 1033 mValues.put(Data.CONTACT_ID, contactId); 1034 Uri dataUri = mContactsProvider.insert(Data.CONTENT_URI, mValues); 1035 dataId = ContentUris.parseId(dataUri); 1036 count = 1; 1037 } else { 1038 Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId); 1039 count = mContactsProvider.update(dataUri, mValues, null, null); 1040 } 1041 1042 mValues.clear(); 1043 OpenHelper.copyStringValue(mValues, LegacyPhotoData.LOCAL_VERSION, 1044 values, android.provider.Contacts.Photos.LOCAL_VERSION); 1045 OpenHelper.copyStringValue(mValues, LegacyPhotoData.DOWNLOAD_REQUIRED, 1046 values, android.provider.Contacts.Photos.DOWNLOAD_REQUIRED); 1047 OpenHelper.copyStringValue(mValues, LegacyPhotoData.EXISTS_ON_SERVER, 1048 values, android.provider.Contacts.Photos.EXISTS_ON_SERVER); 1049 OpenHelper.copyStringValue(mValues, LegacyPhotoData.SYNC_ERROR, 1050 values, android.provider.Contacts.Photos.SYNC_ERROR); 1051 1052 int updated = mContactsProvider.update(Data.CONTENT_URI, mValues, 1053 Data.MIMETYPE + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'" 1054 + " AND " + Data.CONTACT_ID + "=" + contactId 1055 + " AND " + LegacyPhotoData.PHOTO_DATA_ID + "=" + dataId, null); 1056 if (updated == 0) { 1057 mValues.put(Data.CONTACT_ID, contactId); 1058 mValues.put(Data.MIMETYPE, LegacyPhotoData.CONTENT_ITEM_TYPE); 1059 mValues.put(LegacyPhotoData.PHOTO_DATA_ID, dataId); 1060 mContactsProvider.insert(Data.CONTENT_URI, mValues); 1061 } 1062 1063 return count; 1064 } 1065 1066 public int delete(Uri uri, String selection, String[] selectionArgs) { 1067 final int match = sUriMatcher.match(uri); 1068 int count = 0; 1069 switch (match) { 1070 case PEOPLE_ID: 1071 count = mContactsProvider.deleteRawContact(ContentUris.parseId(uri), false); 1072 break; 1073 1074 case ORGANIZATIONS_ID: 1075 count = mContactsProvider.deleteData(ContentUris.parseId(uri), 1076 ORGANIZATION_MIME_TYPES); 1077 break; 1078 1079 case CONTACTMETHODS_ID: 1080 count = mContactsProvider.deleteData(ContentUris.parseId(uri), 1081 CONTACT_METHOD_MIME_TYPES); 1082 break; 1083 1084 case PHONES_ID: 1085 count = mContactsProvider.deleteData(ContentUris.parseId(uri), 1086 PHONE_MIME_TYPES); 1087 break; 1088 1089 default: 1090 throw new UnsupportedOperationException("Unknown uri: " + uri); 1091 } 1092 1093 return count; 1094 } 1095 1096 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 1097 String sortOrder) { 1098 final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 1099 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 1100 String groupBy = null; 1101 String limit = null; 1102 1103 final int match = sUriMatcher.match(uri); 1104 switch (match) { 1105 case PEOPLE: { 1106 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE); 1107 qb.setProjectionMap(sPeopleProjectionMap); 1108 break; 1109 } 1110 1111 case PEOPLE_ID: 1112 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE); 1113 qb.setProjectionMap(sPeopleProjectionMap); 1114 qb.appendWhere(People._ID + "="); 1115 qb.appendWhere(uri.getPathSegments().get(1)); 1116 break; 1117 1118 case PEOPLE_FILTER: { 1119 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE); 1120 qb.setProjectionMap(sPeopleProjectionMap); 1121 String filterParam = uri.getPathSegments().get(2); 1122 qb.appendWhere(People._ID + " IN " 1123 + mContactsProvider.getContactsByFilterAsNestedQuery(filterParam)); 1124 break; 1125 } 1126 1127 case ORGANIZATIONS: 1128 qb.setTables(LegacyTables.ORGANIZATIONS); 1129 qb.setProjectionMap(sOrganizationProjectionMap); 1130 mContactsProvider.applyDataRestrictionExceptions(qb); 1131 break; 1132 1133 case ORGANIZATIONS_ID: 1134 qb.setTables(LegacyTables.ORGANIZATIONS); 1135 qb.setProjectionMap(sOrganizationProjectionMap); 1136 mContactsProvider.applyDataRestrictionExceptions(qb); 1137 qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "="); 1138 qb.appendWhere(uri.getPathSegments().get(1)); 1139 break; 1140 1141 case CONTACTMETHODS: 1142 qb.setTables(LegacyTables.CONTACT_METHODS); 1143 qb.setProjectionMap(sContactMethodProjectionMap); 1144 mContactsProvider.applyDataRestrictionExceptions(qb); 1145 break; 1146 1147 case CONTACTMETHODS_ID: 1148 qb.setTables(LegacyTables.CONTACT_METHODS); 1149 qb.setProjectionMap(sContactMethodProjectionMap); 1150 mContactsProvider.applyDataRestrictionExceptions(qb); 1151 qb.appendWhere(" AND " + ContactMethods._ID + "="); 1152 qb.appendWhere(uri.getPathSegments().get(1)); 1153 break; 1154 1155 case PEOPLE_CONTACTMETHODS: 1156 qb.setTables(LegacyTables.CONTACT_METHODS); 1157 qb.setProjectionMap(sContactMethodProjectionMap); 1158 mContactsProvider.applyDataRestrictionExceptions(qb); 1159 qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "="); 1160 qb.appendWhere(uri.getPathSegments().get(1)); 1161 qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL"); 1162 break; 1163 1164 case PEOPLE_CONTACTMETHODS_ID: 1165 qb.setTables(LegacyTables.CONTACT_METHODS); 1166 qb.setProjectionMap(sContactMethodProjectionMap); 1167 mContactsProvider.applyDataRestrictionExceptions(qb); 1168 qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "="); 1169 qb.appendWhere(uri.getPathSegments().get(1)); 1170 qb.appendWhere(" AND " + ContactMethods._ID + "="); 1171 qb.appendWhere(uri.getPathSegments().get(3)); 1172 qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL"); 1173 break; 1174 1175 case PHONES: 1176 qb.setTables(LegacyTables.PHONES); 1177 qb.setProjectionMap(sPhoneProjectionMap); 1178 mContactsProvider.applyDataRestrictionExceptions(qb); 1179 break; 1180 1181 case PHONES_ID: 1182 qb.setTables(LegacyTables.PHONES); 1183 qb.setProjectionMap(sPhoneProjectionMap); 1184 mContactsProvider.applyDataRestrictionExceptions(qb); 1185 qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "="); 1186 qb.appendWhere(uri.getPathSegments().get(1)); 1187 break; 1188 1189 case PEOPLE_PHONES: 1190 qb.setTables(LegacyTables.PHONES); 1191 qb.setProjectionMap(sPhoneProjectionMap); 1192 mContactsProvider.applyDataRestrictionExceptions(qb); 1193 qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "="); 1194 qb.appendWhere(uri.getPathSegments().get(1)); 1195 break; 1196 1197 case PEOPLE_PHONES_ID: 1198 qb.setTables(LegacyTables.PHONES); 1199 qb.setProjectionMap(sPhoneProjectionMap); 1200 mContactsProvider.applyDataRestrictionExceptions(qb); 1201 qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "="); 1202 qb.appendWhere(uri.getPathSegments().get(1)); 1203 qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "="); 1204 qb.appendWhere(uri.getPathSegments().get(3)); 1205 break; 1206 1207 case EXTENSIONS: 1208 qb.setTables(LegacyTables.EXTENSIONS); 1209 qb.setProjectionMap(sExtensionProjectionMap); 1210 mContactsProvider.applyDataRestrictionExceptions(qb); 1211 break; 1212 1213 case EXTENSIONS_ID: 1214 qb.setTables(LegacyTables.EXTENSIONS); 1215 qb.setProjectionMap(sExtensionProjectionMap); 1216 mContactsProvider.applyDataRestrictionExceptions(qb); 1217 qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "="); 1218 qb.appendWhere(uri.getPathSegments().get(1)); 1219 break; 1220 1221 case PEOPLE_EXTENSIONS: 1222 qb.setTables(LegacyTables.EXTENSIONS); 1223 qb.setProjectionMap(sExtensionProjectionMap); 1224 mContactsProvider.applyDataRestrictionExceptions(qb); 1225 qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "="); 1226 qb.appendWhere(uri.getPathSegments().get(1)); 1227 break; 1228 1229 case PEOPLE_EXTENSIONS_ID: 1230 qb.setTables(LegacyTables.EXTENSIONS); 1231 qb.setProjectionMap(sExtensionProjectionMap); 1232 mContactsProvider.applyDataRestrictionExceptions(qb); 1233 qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "="); 1234 qb.appendWhere(uri.getPathSegments().get(1)); 1235 qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "="); 1236 qb.appendWhere(uri.getPathSegments().get(3)); 1237 break; 1238 1239 case GROUPS: 1240 qb.setTables(LegacyTables.GROUPS); 1241 qb.setProjectionMap(sGroupProjectionMap); 1242 break; 1243 1244 case GROUPS_ID: 1245 qb.setTables(LegacyTables.GROUPS); 1246 qb.setProjectionMap(sGroupProjectionMap); 1247 qb.appendWhere(android.provider.Contacts.Groups._ID + "="); 1248 qb.appendWhere(uri.getPathSegments().get(1)); 1249 break; 1250 1251 case GROUPMEMBERSHIP: 1252 qb.setTables(LegacyTables.GROUP_MEMBERSHIP); 1253 qb.setProjectionMap(sGroupMembershipProjectionMap); 1254 mContactsProvider.applyDataRestrictionExceptions(qb); 1255 break; 1256 1257 case GROUPMEMBERSHIP_ID: 1258 qb.setTables(LegacyTables.GROUP_MEMBERSHIP); 1259 qb.setProjectionMap(sGroupMembershipProjectionMap); 1260 mContactsProvider.applyDataRestrictionExceptions(qb); 1261 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "="); 1262 qb.appendWhere(uri.getPathSegments().get(1)); 1263 break; 1264 1265 case PEOPLE_GROUPMEMBERSHIP: 1266 qb.setTables(LegacyTables.GROUP_MEMBERSHIP); 1267 qb.setProjectionMap(sGroupMembershipProjectionMap); 1268 mContactsProvider.applyDataRestrictionExceptions(qb); 1269 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "="); 1270 qb.appendWhere(uri.getPathSegments().get(1)); 1271 break; 1272 1273 case PEOPLE_GROUPMEMBERSHIP_ID: 1274 qb.setTables(LegacyTables.GROUP_MEMBERSHIP); 1275 qb.setProjectionMap(sGroupMembershipProjectionMap); 1276 mContactsProvider.applyDataRestrictionExceptions(qb); 1277 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "="); 1278 qb.appendWhere(uri.getPathSegments().get(1)); 1279 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "="); 1280 qb.appendWhere(uri.getPathSegments().get(3)); 1281 break; 1282 1283 case PEOPLE_PHOTO: 1284 qb.setTables(LegacyTables.PHOTOS); 1285 qb.setProjectionMap(sPhotoProjectionMap); 1286 mContactsProvider.applyDataRestrictionExceptions(qb); 1287 qb.appendWhere(" AND " + android.provider.Contacts.Photos.PERSON_ID + "="); 1288 qb.appendWhere(uri.getPathSegments().get(1)); 1289 limit = "1"; 1290 break; 1291 1292 case PRESENCE: 1293 qb.setTables(Tables.PRESENCE); 1294 qb.setProjectionMap(sPresenceProjectionMap); 1295 qb.appendWhere(mContactsProvider.getContactsRestrictionExceptionAsNestedQuery( 1296 android.provider.Contacts.Presence.PERSON_ID)); 1297 break; 1298 1299 case PRESENCE_ID: 1300 qb.setTables(Tables.PRESENCE); 1301 qb.setProjectionMap(sPresenceProjectionMap); 1302 qb.appendWhere(mContactsProvider.getContactsRestrictionExceptionAsNestedQuery( 1303 android.provider.Contacts.Presence.PERSON_ID)); 1304 qb.appendWhere(" AND " + android.provider.Contacts.Presence._ID + "="); 1305 qb.appendWhere(uri.getPathSegments().get(1)); 1306 break; 1307 1308 case DELETED_PEOPLE: 1309 case DELETED_GROUPS: 1310 throw new UnsupportedOperationException(); 1311 1312 default: 1313 throw new IllegalArgumentException("Unknown URL " + uri); 1314 } 1315 1316 // Perform the query and set the notification uri 1317 final Cursor c = qb.query(db, projection, selection, selectionArgs, 1318 groupBy, null, sortOrder, limit); 1319 if (c != null) { 1320 c.setNotificationUri(mContext.getContentResolver(), RawContacts.CONTENT_URI); 1321 } 1322 DatabaseUtils.dumpCursor(c); 1323 return c; 1324 } 1325 1326 /** 1327 * Called when a change has been made. 1328 * 1329 * @param uri the uri that the change was made to 1330 */ 1331 private void onChange(Uri uri) { 1332 mContext.getContentResolver().notifyChange(android.provider.Contacts.CONTENT_URI, null); 1333 } 1334} 1335