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