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