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