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