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