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