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