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 17 package com.android.providers.contacts; 18 19 import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns; 20 import com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns; 21 import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns; 22 import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; 23 import com.android.providers.contacts.ContactsDatabaseHelper.Tables; 24 25 import android.content.ContentResolver; 26 import android.content.ContentValues; 27 import android.content.Context; 28 import android.database.Cursor; 29 import android.database.DatabaseUtils; 30 import android.database.sqlite.SQLiteDatabase; 31 import android.database.sqlite.SQLiteException; 32 import android.database.sqlite.SQLiteStatement; 33 import android.provider.CallLog.Calls; 34 import android.provider.ContactsContract.Contacts; 35 import android.provider.ContactsContract.Data; 36 import android.provider.ContactsContract.Groups; 37 import android.provider.ContactsContract.RawContacts; 38 import android.provider.ContactsContract.CommonDataKinds.Email; 39 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 40 import android.provider.ContactsContract.CommonDataKinds.Im; 41 import android.provider.ContactsContract.CommonDataKinds.Note; 42 import android.provider.ContactsContract.CommonDataKinds.Organization; 43 import android.provider.ContactsContract.CommonDataKinds.Phone; 44 import android.provider.ContactsContract.CommonDataKinds.Photo; 45 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 46 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 47 import android.telephony.PhoneNumberUtils; 48 import android.text.TextUtils; 49 import android.util.Log; 50 51 import java.io.File; 52 53 public class LegacyContactImporter { 54 55 public static final String TAG = "LegacyContactImporter"; 56 57 private static final int MAX_ATTEMPTS = 5; 58 private static final int DELAY_BETWEEN_ATTEMPTS = 2000; 59 60 public static final String DEFAULT_ACCOUNT_TYPE = "com.google"; 61 private static final String DATABASE_NAME = "contacts.db"; 62 63 private static final int INSERT_BATCH_SIZE = 200; 64 65 /** 66 * Estimated increase in database size after import. 67 */ 68 private static final long DATABASE_SIZE_MULTIPLIER = 4; 69 70 /** 71 * Estimated minimum database size in megabytes. 72 */ 73 private static final long DATABASE_MIN_SIZE = 5; 74 75 private final Context mContext; 76 private final ContactsProvider2 mContactsProvider; 77 private final NameLookupBuilder mNameLookupBuilder; 78 79 private ContactsDatabaseHelper mDbHelper; 80 private ContentValues mValues = new ContentValues(); 81 private ContentResolver mResolver; 82 private boolean mPhoneticNameAvailable = true; 83 84 private SQLiteDatabase mSourceDb; 85 private SQLiteDatabase mTargetDb; 86 87 private NameSplitter mNameSplitter; 88 private int mBatchCounter; 89 90 private int mContactCount; 91 92 private long mStructuredNameMimetypeId; 93 private long mNoteMimetypeId; 94 private long mOrganizationMimetypeId; 95 private long mPhoneMimetypeId; 96 private long mEmailMimetypeId; 97 private long mImMimetypeId; 98 private long mPostalMimetypeId; 99 private long mPhotoMimetypeId; 100 private long mGroupMembershipMimetypeId; 101 102 private long mEstimatedStorageRequirement = DATABASE_MIN_SIZE; 103 104 public LegacyContactImporter(Context context, ContactsProvider2 contactsProvider) { 105 mContext = context; 106 mContactsProvider = contactsProvider; 107 mResolver = mContactsProvider.getContext().getContentResolver(); 108 mNameLookupBuilder = mContactsProvider.getNameLookupBuilder(); 109 } 110 111 public boolean importContacts() throws Exception { 112 String path = mContext.getDatabasePath(DATABASE_NAME).getPath(); 113 File file = new File(path); 114 if (!file.exists()) { 115 Log.i(TAG, "Legacy contacts database does not exist at " + path); 116 return true; 117 } 118 119 Log.w(TAG, "Importing contacts from " + path); 120 121 for (int i = 0; i < MAX_ATTEMPTS; i++) { 122 try { 123 mSourceDb = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY); 124 importContactsFromLegacyDb(); 125 Log.i(TAG, "Imported legacy contacts: " + mContactCount); 126 mContactsProvider.notifyChange(); 127 return true; 128 129 } catch (SQLiteException e) { 130 Log.e(TAG, "Database import exception. Will retry in " + DELAY_BETWEEN_ATTEMPTS 131 + "ms", e); 132 133 // We could get a "database locked" exception here, in which 134 // case we should retry 135 Thread.sleep(DELAY_BETWEEN_ATTEMPTS); 136 137 } finally { 138 if (mSourceDb != null) { 139 mSourceDb.close(); 140 } 141 } 142 } 143 144 long oldDatabaseSize = file.length(); 145 mEstimatedStorageRequirement = oldDatabaseSize * DATABASE_SIZE_MULTIPLIER / 1024 / 1024; 146 if (mEstimatedStorageRequirement < DATABASE_MIN_SIZE) { 147 mEstimatedStorageRequirement = DATABASE_MIN_SIZE; 148 } 149 150 return false; 151 } 152 153 public long getEstimatedStorageRequirement() { 154 return mEstimatedStorageRequirement; 155 } 156 157 private void importContactsFromLegacyDb() { 158 int version = mSourceDb.getVersion(); 159 160 // Upgrade to version 78 was the latest that wiped out data. Might as well follow suit 161 // and ignore earlier versions. 162 if (version < 78) { 163 return; 164 } 165 166 if (version < 80) { 167 mPhoneticNameAvailable = false; 168 } 169 170 mDbHelper = (ContactsDatabaseHelper)mContactsProvider.getDatabaseHelper(); 171 mTargetDb = mDbHelper.getWritableDatabase(); 172 173 mStructuredNameMimetypeId = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE); 174 mNoteMimetypeId = mDbHelper.getMimeTypeId(Note.CONTENT_ITEM_TYPE); 175 mOrganizationMimetypeId = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE); 176 mPhoneMimetypeId = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE); 177 mEmailMimetypeId = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE); 178 mImMimetypeId = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE); 179 mPostalMimetypeId = mDbHelper.getMimeTypeId(StructuredPostal.CONTENT_ITEM_TYPE); 180 mPhotoMimetypeId = mDbHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE); 181 mGroupMembershipMimetypeId = 182 mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE); 183 184 mNameSplitter = mContactsProvider.getNameSplitter(); 185 186 mTargetDb.beginTransaction(); 187 try { 188 checkForImportFailureTest(); 189 190 /* 191 * At this point there should be no data in the contacts provider, but in case 192 * some was inserted by mistake, we should remove it. The main reason for this 193 * is that we will be preserving original contact IDs and don't want to run into 194 * any collisions. 195 */ 196 mContactsProvider.wipeData(); 197 198 importGroups(); 199 importPeople(); 200 importOrganizations(); 201 importPhones(); 202 importContactMethods(); 203 importPhotos(); 204 importGroupMemberships(); 205 updateDisplayNamesAndLookupKeys(); 206 207 // Deleted contacts should be inserted after everything else, because 208 // the legacy table does not provide an _ID field - the _ID field 209 // will be autoincremented 210 importDeletedPeople(); 211 212 mDbHelper.updateAllVisible(); 213 214 mTargetDb.setTransactionSuccessful(); 215 } finally { 216 mTargetDb.endTransaction(); 217 } 218 219 importCalls(); 220 } 221 222 /** 223 * This is used for simulating an import failure. Insert a row into the "settings" 224 * table with key='TEST' and then proceed with the upgrade. Remove the record 225 * after verifying the failure handling. 226 */ 227 private void checkForImportFailureTest() { 228 long isTest = DatabaseUtils.longForQuery(mSourceDb, 229 "SELECT COUNT(*) FROM settings WHERE key='TEST'", null); 230 if (isTest != 0) { 231 throw new SQLiteException("Testing import failure."); 232 } 233 } 234 235 private interface GroupsQuery { 236 String TABLE = "groups"; 237 238 String[] COLUMNS = { 239 "_id", "name", "notes", "should_sync", "system_id", "_sync_account", "_sync_id", 240 "_sync_dirty", 241 }; 242 243 static int ID = 0; 244 static int NAME = 1; 245 static int NOTES = 2; 246 static int SHOULD_SYNC = 3; // TODO add this feature to Groups 247 static int SYSTEM_ID = 4; 248 249 static int _SYNC_ACCOUNT = 5; 250 static int _SYNC_ID = 6; 251 static int _SYNC_DIRTY = 7; 252 } 253 254 private interface GroupsInsert { 255 String INSERT_SQL = "INSERT INTO " + Tables.GROUPS + "(" + 256 Groups._ID + "," + 257 Groups.TITLE + "," + 258 Groups.NOTES + "," + 259 Groups.SYSTEM_ID + "," + 260 Groups.DIRTY + "," + 261 Groups.GROUP_VISIBLE + "," + 262 Groups.ACCOUNT_NAME + "," + 263 Groups.ACCOUNT_TYPE + "," + 264 Groups.SOURCE_ID + 265 ") VALUES (?,?,?,?,?,?,?,?,?)"; 266 267 int ID = 1; 268 int TITLE = 2; 269 int NOTES = 3; 270 int SYSTEM_ID = 4; 271 int DIRTY = 5; 272 int GROUP_VISIBLE = 6; 273 int ACCOUNT_NAME = 7; 274 int ACCOUNT_TYPE = 8; 275 int SOURCE_ID = 9; 276 } 277 278 private void importGroups() { 279 SQLiteStatement insert = mTargetDb.compileStatement(GroupsInsert.INSERT_SQL); 280 Cursor c = mSourceDb.query(GroupsQuery.TABLE, GroupsQuery.COLUMNS, null, null, 281 null, null, null); 282 try { 283 while (c.moveToNext()) { 284 insertGroup(c, insert); 285 } 286 } finally { 287 c.close(); 288 insert.close(); 289 } 290 } 291 292 private void insertGroup(Cursor c, SQLiteStatement insert) { 293 long id = c.getLong(GroupsQuery.ID); 294 295 insert.bindLong(GroupsInsert.ID, id); 296 bindString(insert, GroupsInsert.TITLE, c.getString(GroupsQuery.NAME)); 297 bindString(insert, GroupsInsert.NOTES, c.getString(GroupsQuery.NOTES)); 298 bindString(insert, GroupsInsert.SYSTEM_ID, c.getString(GroupsQuery.SYSTEM_ID)); 299 insert.bindLong(GroupsInsert.DIRTY, c.getLong(GroupsQuery._SYNC_DIRTY)); 300 insert.bindLong(GroupsInsert.GROUP_VISIBLE, 1); 301 302 String account = c.getString(GroupsQuery._SYNC_ACCOUNT); 303 if (!TextUtils.isEmpty(account)) { 304 bindString(insert, GroupsInsert.ACCOUNT_NAME, account); 305 bindString(insert, GroupsInsert.ACCOUNT_TYPE, DEFAULT_ACCOUNT_TYPE); 306 bindString(insert, GroupsInsert.SOURCE_ID, c.getString(GroupsQuery._SYNC_ID)); 307 } else { 308 insert.bindNull(GroupsInsert.ACCOUNT_NAME); 309 insert.bindNull(GroupsInsert.ACCOUNT_TYPE); 310 insert.bindNull(GroupsInsert.SOURCE_ID); 311 } 312 insert(insert); 313 } 314 315 private interface PeopleQuery { 316 String TABLE = "people"; 317 318 String NAME_SQL = 319 "(CASE WHEN (name IS NOT NULL AND name != '') " 320 + "THEN name " 321 + "ELSE " 322 + "(CASE WHEN primary_organization is NOT NULL THEN " 323 + "(SELECT company FROM organizations WHERE " 324 + "organizations._id = primary_organization) " 325 + "ELSE " 326 + "(CASE WHEN primary_phone IS NOT NULL THEN " 327 +"(SELECT number FROM phones WHERE phones._id = primary_phone) " 328 + "ELSE " 329 + "(CASE WHEN primary_email IS NOT NULL THEN " 330 + "(SELECT data FROM contact_methods WHERE " 331 + "contact_methods._id = primary_email) " 332 + "ELSE " 333 + "null " 334 + "END) " 335 + "END) " 336 + "END) " 337 + "END) "; 338 339 340 String[] COLUMNS_WITH_DISPLAY_NAME_WITHOUT_PHONETIC_NAME = { 341 "_id", NAME_SQL, "notes", "times_contacted", "last_time_contacted", "starred", 342 "primary_phone", "primary_organization", "primary_email", "custom_ringtone", 343 "send_to_voicemail", "_sync_account", "_sync_id", "_sync_time", "_sync_local_id", 344 "_sync_dirty", 345 }; 346 347 String[] COLUMNS_WITH_DISPLAY_NAME_WITH_PHONETIC_NAME = { 348 "_id", NAME_SQL, "notes", "times_contacted", "last_time_contacted", "starred", 349 "primary_phone", "primary_organization", "primary_email", "custom_ringtone", 350 "send_to_voicemail", "_sync_account", "_sync_id", "_sync_time", "_sync_local_id", 351 "_sync_dirty", "phonetic_name", 352 }; 353 354 String[] COLUMNS_WITHOUT_PHONETIC_NAME = { 355 "_id", "name", "notes", "times_contacted", "last_time_contacted", "starred", 356 "primary_phone", "primary_organization", "primary_email", "custom_ringtone", 357 "send_to_voicemail", "_sync_account", "_sync_id", "_sync_time", "_sync_local_id", 358 "_sync_dirty", 359 }; 360 361 String[] COLUMNS_WITH_PHONETIC_NAME = { 362 "_id", "name", "notes", "times_contacted", "last_time_contacted", "starred", 363 "primary_phone", "primary_organization", "primary_email", "custom_ringtone", 364 "send_to_voicemail", "_sync_account", "_sync_id", "_sync_time", "_sync_local_id", 365 "_sync_dirty", "phonetic_name", 366 }; 367 368 static int _ID = 0; 369 static int NAME = 1; 370 static int NOTES = 2; 371 static int TIMES_CONTACTED = 3; 372 static int LAST_TIME_CONTACTED = 4; 373 static int STARRED = 5; 374 static int PRIMARY_PHONE = 6; 375 static int PRIMARY_ORGANIZATION = 7; 376 static int PRIMARY_EMAIL = 8; 377 static int CUSTOM_RINGTONE = 9; 378 static int SEND_TO_VOICEMAIL = 10; 379 380 static int _SYNC_ACCOUNT = 11; 381 static int _SYNC_ID = 12; 382 static int _SYNC_TIME = 13; 383 static int _SYNC_LOCAL_ID = 14; 384 static int _SYNC_DIRTY = 15; 385 386 static int PHONETIC_NAME = 16; 387 } 388 389 390 private interface RawContactsInsert { 391 String INSERT_SQL = "INSERT INTO " + Tables.RAW_CONTACTS + "(" + 392 RawContacts._ID + "," + 393 RawContacts.CONTACT_ID + "," + 394 RawContacts.CUSTOM_RINGTONE + "," + 395 RawContacts.DIRTY + "," + 396 RawContacts.LAST_TIME_CONTACTED + "," + 397 RawContacts.SEND_TO_VOICEMAIL + "," + 398 RawContacts.STARRED + "," + 399 RawContacts.TIMES_CONTACTED + "," + 400 RawContacts.SYNC1 + "," + 401 RawContacts.SYNC2 + "," + 402 RawContacts.ACCOUNT_NAME + "," + 403 RawContacts.ACCOUNT_TYPE + "," + 404 RawContacts.SOURCE_ID + "," + 405 RawContactsColumns.DISPLAY_NAME + 406 ") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; 407 408 int ID = 1; 409 int CONTACT_ID = 2; 410 int CUSTOM_RINGTONE = 3; 411 int DIRTY = 4; 412 int LAST_TIME_CONTACTED = 5; 413 int SEND_TO_VOICEMAIL = 6; 414 int STARRED = 7; 415 int TIMES_CONTACTED = 8; 416 int SYNC1 = 9; 417 int SYNC2 = 10; 418 int ACCOUNT_NAME = 11; 419 int ACCOUNT_TYPE = 12; 420 int SOURCE_ID = 13; 421 int DISPLAY_NAME = 14; 422 } 423 424 private interface ContactsInsert { 425 String INSERT_SQL = "INSERT INTO " + Tables.CONTACTS + "(" + 426 Contacts._ID + "," + 427 Contacts.CUSTOM_RINGTONE + "," + 428 Contacts.LAST_TIME_CONTACTED + "," + 429 Contacts.SEND_TO_VOICEMAIL + "," + 430 Contacts.STARRED + "," + 431 Contacts.TIMES_CONTACTED + "," + 432 Contacts.NAME_RAW_CONTACT_ID + 433 ") VALUES (?,?,?,?,?,?,?)"; 434 435 int ID = 1; 436 int CUSTOM_RINGTONE = 2; 437 int LAST_TIME_CONTACTED = 3; 438 int SEND_TO_VOICEMAIL = 4; 439 int STARRED = 5; 440 int TIMES_CONTACTED = 6; 441 int NAME_RAW_CONTACT_ID = 7; 442 } 443 444 private interface StructuredNameInsert { 445 String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" + 446 Data.RAW_CONTACT_ID + "," + 447 DataColumns.MIMETYPE_ID + "," + 448 StructuredName.DISPLAY_NAME + "," + 449 StructuredName.PREFIX + "," + 450 StructuredName.GIVEN_NAME + "," + 451 StructuredName.MIDDLE_NAME + "," + 452 StructuredName.FAMILY_NAME + "," + 453 StructuredName.SUFFIX + "," + 454 StructuredName.FULL_NAME_STYLE + "," + 455 StructuredName.PHONETIC_FAMILY_NAME + "," + 456 StructuredName.PHONETIC_MIDDLE_NAME + "," + 457 StructuredName.PHONETIC_GIVEN_NAME + "," + 458 StructuredName.PHONETIC_NAME_STYLE + 459 ") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)"; 460 461 int RAW_CONTACT_ID = 1; 462 int MIMETYPE_ID = 2; 463 int DISPLAY_NAME = 3; 464 int PREFIX = 4; 465 int GIVEN_NAME = 5; 466 int MIDDLE_NAME = 6; 467 int FAMILY_NAME = 7; 468 int SUFFIX = 8; 469 int FULL_NAME_STYLE = 9; 470 int PHONETIC_FAMILY_NAME = 10; 471 int PHONETIC_MIDDLE_NAME = 11; 472 int PHONETIC_GIVEN_NAME = 12; 473 int PHONETIC_NAME_STYLE = 13; 474 } 475 476 private interface NoteInsert { 477 String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" + 478 Data.RAW_CONTACT_ID + "," + 479 DataColumns.MIMETYPE_ID + "," + 480 Note.NOTE + 481 ") VALUES (?,?,?)"; 482 483 int RAW_CONTACT_ID = 1; 484 int MIMETYPE_ID = 2; 485 int NOTE = 3; 486 } 487 488 private void importPeople() { 489 SQLiteStatement rawContactInsert = mTargetDb.compileStatement(RawContactsInsert.INSERT_SQL); 490 SQLiteStatement contactInsert = mTargetDb.compileStatement(ContactsInsert.INSERT_SQL); 491 SQLiteStatement structuredNameInsert = 492 mTargetDb.compileStatement(StructuredNameInsert.INSERT_SQL); 493 SQLiteStatement noteInsert = mTargetDb.compileStatement(NoteInsert.INSERT_SQL); 494 try { 495 String[] columns = mPhoneticNameAvailable 496 ? PeopleQuery.COLUMNS_WITH_DISPLAY_NAME_WITH_PHONETIC_NAME 497 : PeopleQuery.COLUMNS_WITH_DISPLAY_NAME_WITHOUT_PHONETIC_NAME; 498 Cursor c = mSourceDb.query(PeopleQuery.TABLE, columns, "name IS NULL", null, null, 499 null, null); 500 try { 501 while (c.moveToNext()) { 502 insertRawContact(c, rawContactInsert); 503 insertContact(c, contactInsert); 504 insertNote(c, noteInsert); 505 mContactCount++; 506 } 507 } finally { 508 c.close(); 509 } 510 511 columns = mPhoneticNameAvailable 512 ? PeopleQuery.COLUMNS_WITH_PHONETIC_NAME 513 : PeopleQuery.COLUMNS_WITHOUT_PHONETIC_NAME; 514 c = mSourceDb.query(PeopleQuery.TABLE, columns, "name IS NOT NULL", null, null, null, 515 null); 516 try { 517 while (c.moveToNext()) { 518 long id = insertRawContact(c, rawContactInsert); 519 insertContact(c, contactInsert); 520 insertStructuredName(c, structuredNameInsert); 521 insertNote(c, noteInsert); 522 mContactCount++; 523 } 524 } finally { 525 c.close(); 526 } 527 } finally { 528 rawContactInsert.close(); 529 contactInsert.close(); 530 structuredNameInsert.close(); 531 noteInsert.close(); 532 } 533 } 534 535 private long insertRawContact(Cursor c, SQLiteStatement insert) { 536 long id = c.getLong(PeopleQuery._ID); 537 insert.bindLong(RawContactsInsert.ID, id); 538 insert.bindLong(RawContactsInsert.CONTACT_ID, id); 539 bindString(insert, RawContactsInsert.CUSTOM_RINGTONE, 540 c.getString(PeopleQuery.CUSTOM_RINGTONE)); 541 bindString(insert, RawContactsInsert.DIRTY, 542 c.getString(PeopleQuery._SYNC_DIRTY)); 543 insert.bindLong(RawContactsInsert.LAST_TIME_CONTACTED, 544 c.getLong(PeopleQuery.LAST_TIME_CONTACTED)); 545 insert.bindLong(RawContactsInsert.SEND_TO_VOICEMAIL, 546 c.getLong(PeopleQuery.SEND_TO_VOICEMAIL)); 547 insert.bindLong(RawContactsInsert.STARRED, 548 c.getLong(PeopleQuery.STARRED)); 549 insert.bindLong(RawContactsInsert.TIMES_CONTACTED, 550 c.getLong(PeopleQuery.TIMES_CONTACTED)); 551 bindString(insert, RawContactsInsert.SYNC1, 552 c.getString(PeopleQuery._SYNC_TIME)); 553 bindString(insert, RawContactsInsert.SYNC2, 554 c.getString(PeopleQuery._SYNC_LOCAL_ID)); 555 bindString(insert, RawContactsInsert.DISPLAY_NAME, 556 c.getString(PeopleQuery.NAME)); 557 558 String account = c.getString(PeopleQuery._SYNC_ACCOUNT); 559 if (!TextUtils.isEmpty(account)) { 560 bindString(insert, RawContactsInsert.ACCOUNT_NAME, account); 561 bindString(insert, RawContactsInsert.ACCOUNT_TYPE, DEFAULT_ACCOUNT_TYPE); 562 bindString(insert, RawContactsInsert.SOURCE_ID, c.getString(PeopleQuery._SYNC_ID)); 563 } else { 564 insert.bindNull(RawContactsInsert.ACCOUNT_NAME); 565 insert.bindNull(RawContactsInsert.ACCOUNT_TYPE); 566 insert.bindNull(RawContactsInsert.SOURCE_ID); 567 } 568 insert(insert); 569 return id; 570 } 571 572 private void insertContact(Cursor c, SQLiteStatement insert) { 573 long id = c.getLong(PeopleQuery._ID); 574 insert.bindLong(ContactsInsert.ID, id); 575 bindString(insert, ContactsInsert.CUSTOM_RINGTONE, 576 c.getString(PeopleQuery.CUSTOM_RINGTONE)); 577 insert.bindLong(ContactsInsert.LAST_TIME_CONTACTED, 578 c.getLong(PeopleQuery.LAST_TIME_CONTACTED)); 579 insert.bindLong(ContactsInsert.SEND_TO_VOICEMAIL, 580 c.getLong(PeopleQuery.SEND_TO_VOICEMAIL)); 581 insert.bindLong(ContactsInsert.STARRED, 582 c.getLong(PeopleQuery.STARRED)); 583 insert.bindLong(ContactsInsert.TIMES_CONTACTED, 584 c.getLong(PeopleQuery.TIMES_CONTACTED)); 585 insert.bindLong(ContactsInsert.NAME_RAW_CONTACT_ID, id); 586 587 insert(insert); 588 } 589 590 private void insertStructuredName(Cursor c, SQLiteStatement insert) { 591 String name = c.getString(PeopleQuery.NAME); 592 if (TextUtils.isEmpty(name)) { 593 return; 594 } 595 596 long id = c.getLong(PeopleQuery._ID); 597 598 insert.bindLong(StructuredNameInsert.RAW_CONTACT_ID, id); 599 insert.bindLong(StructuredNameInsert.MIMETYPE_ID, mStructuredNameMimetypeId); 600 bindString(insert, StructuredNameInsert.DISPLAY_NAME, name); 601 602 NameSplitter.Name splitName = new NameSplitter.Name(); 603 mNameSplitter.split(splitName, name); 604 605 bindString(insert, StructuredNameInsert.PREFIX, 606 splitName.getPrefix()); 607 bindString(insert, StructuredNameInsert.GIVEN_NAME, 608 splitName.getGivenNames()); 609 bindString(insert, StructuredNameInsert.MIDDLE_NAME, 610 splitName.getMiddleName()); 611 bindString(insert, StructuredNameInsert.FAMILY_NAME, 612 splitName.getFamilyName()); 613 bindString(insert, StructuredNameInsert.SUFFIX, 614 splitName.getSuffix()); 615 final String joined = mNameSplitter.join(splitName, true, true); 616 bindString(insert, StructuredNameInsert.DISPLAY_NAME, joined); 617 618 if (mPhoneticNameAvailable) { 619 String phoneticName = c.getString(PeopleQuery.PHONETIC_NAME); 620 if (phoneticName != null) { 621 int index = phoneticName.indexOf(' '); 622 if (index == -1) { 623 splitName.phoneticFamilyName = phoneticName; 624 } else { 625 splitName.phoneticFamilyName = phoneticName.substring(0, index).trim(); 626 splitName.phoneticGivenName = phoneticName.substring(index + 1).trim(); 627 } 628 } 629 } 630 631 mNameSplitter.guessNameStyle(splitName); 632 633 int fullNameStyle = splitName.getFullNameStyle(); 634 insert.bindLong(StructuredNameInsert.FULL_NAME_STYLE, 635 fullNameStyle); 636 bindString(insert, StructuredNameInsert.PHONETIC_FAMILY_NAME, 637 splitName.phoneticFamilyName); 638 bindString(insert, StructuredNameInsert.PHONETIC_MIDDLE_NAME, 639 splitName.phoneticMiddleName); 640 bindString(insert, StructuredNameInsert.PHONETIC_GIVEN_NAME, 641 splitName.phoneticGivenName); 642 insert.bindLong(StructuredNameInsert.PHONETIC_NAME_STYLE, 643 splitName.phoneticNameStyle); 644 645 long dataId = insert(insert); 646 647 mNameLookupBuilder.insertNameLookup(id, dataId, name, 648 mNameSplitter.getAdjustedFullNameStyle(fullNameStyle)); 649 650 if (splitName.phoneticFamilyName != null 651 || splitName.phoneticMiddleName != null 652 || splitName.phoneticGivenName != null) { 653 mDbHelper.insertNameLookupForPhoneticName(id, dataId, 654 splitName.phoneticFamilyName, 655 splitName.phoneticMiddleName, 656 splitName.phoneticGivenName); 657 } 658 } 659 660 private void insertNote(Cursor c, SQLiteStatement insert) { 661 String notes = c.getString(PeopleQuery.NOTES); 662 663 if (TextUtils.isEmpty(notes)) { 664 return; 665 } 666 667 long id = c.getLong(PeopleQuery._ID); 668 insert.bindLong(NoteInsert.RAW_CONTACT_ID, id); 669 insert.bindLong(NoteInsert.MIMETYPE_ID, mNoteMimetypeId); 670 bindString(insert, NoteInsert.NOTE, notes); 671 insert(insert); 672 } 673 674 private interface OrganizationsQuery { 675 String TABLE = "organizations"; 676 677 String[] COLUMNS = { 678 "person", "company", "title", "isprimary", "type", "label", 679 }; 680 681 static int PERSON = 0; 682 static int COMPANY = 1; 683 static int TITLE = 2; 684 static int ISPRIMARY = 3; 685 static int TYPE = 4; 686 static int LABEL = 5; 687 } 688 689 private interface OrganizationInsert { 690 String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" + 691 Data.RAW_CONTACT_ID + "," + 692 DataColumns.MIMETYPE_ID + "," + 693 Data.IS_PRIMARY + "," + 694 Data.IS_SUPER_PRIMARY + "," + 695 Organization.COMPANY + "," + 696 Organization.TITLE + "," + 697 Organization.TYPE + "," + 698 Organization.LABEL + 699 ") VALUES (?,?,?,?,?,?,?,?)"; 700 701 int RAW_CONTACT_ID = 1; 702 int MIMETYPE_ID = 2; 703 int IS_PRIMARY = 3; 704 int IS_SUPER_PRIMARY = 4; 705 int COMPANY = 5; 706 int TITLE = 6; 707 int TYPE = 7; 708 int LABEL = 8; 709 } 710 711 private void importOrganizations() { 712 SQLiteStatement insert = mTargetDb.compileStatement(OrganizationInsert.INSERT_SQL); 713 Cursor c = mSourceDb.query(OrganizationsQuery.TABLE, OrganizationsQuery.COLUMNS, null, null, 714 null, null, null); 715 try { 716 while (c.moveToNext()) { 717 insertOrganization(c, insert); 718 } 719 } finally { 720 c.close(); 721 insert.close(); 722 } 723 } 724 725 private void insertOrganization(Cursor c, SQLiteStatement insert) { 726 long id = c.getLong(OrganizationsQuery.PERSON); 727 insert.bindLong(OrganizationInsert.RAW_CONTACT_ID, id); 728 insert.bindLong(OrganizationInsert.MIMETYPE_ID, mOrganizationMimetypeId); 729 bindString(insert, OrganizationInsert.IS_PRIMARY, c.getString(OrganizationsQuery.ISPRIMARY)); 730 bindString(insert, OrganizationInsert.IS_SUPER_PRIMARY, 731 c.getString(OrganizationsQuery.ISPRIMARY)); 732 bindString(insert, OrganizationInsert.COMPANY, c.getString(OrganizationsQuery.COMPANY)); 733 bindString(insert, OrganizationInsert.TITLE, c.getString(OrganizationsQuery.TITLE)); 734 bindString(insert, OrganizationInsert.TYPE, c.getString(OrganizationsQuery.TYPE)); 735 bindString(insert, OrganizationInsert.LABEL, c.getString(OrganizationsQuery.LABEL)); 736 insert(insert); 737 } 738 739 private interface ContactMethodsQuery { 740 String TABLE = "contact_methods"; 741 742 String[] COLUMNS = { 743 "person", "kind", "data", "aux_data", "type", "label", "isprimary", 744 }; 745 746 static int PERSON = 0; 747 static int KIND = 1; 748 static int DATA = 2; 749 static int AUX_DATA = 3; 750 static int TYPE = 4; 751 static int LABEL = 5; 752 static int ISPRIMARY = 6; 753 } 754 755 private interface EmailInsert { 756 String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" + 757 Data.RAW_CONTACT_ID + "," + 758 DataColumns.MIMETYPE_ID + "," + 759 Data.IS_PRIMARY + "," + 760 Data.IS_SUPER_PRIMARY + "," + 761 Email.DATA + "," + 762 Email.TYPE + "," + 763 Email.LABEL + "," + 764 Data.DATA14 + 765 ") VALUES (?,?,?,?,?,?,?,?)"; 766 767 int RAW_CONTACT_ID = 1; 768 int MIMETYPE_ID = 2; 769 int IS_PRIMARY = 3; 770 int IS_SUPER_PRIMARY = 4; 771 int DATA = 5; 772 int TYPE = 6; 773 int LABEL = 7; 774 int AUX_DATA = 8; 775 } 776 777 private interface ImInsert { 778 String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" + 779 Data.RAW_CONTACT_ID + "," + 780 DataColumns.MIMETYPE_ID + "," + 781 Data.IS_PRIMARY + "," + 782 Data.IS_SUPER_PRIMARY + "," + 783 Im.DATA + "," + 784 Im.TYPE + "," + 785 Im.LABEL + "," + 786 Data.DATA14 + 787 ") VALUES (?,?,?,?,?,?,?,?)"; 788 789 int RAW_CONTACT_ID = 1; 790 int MIMETYPE_ID = 2; 791 int IS_PRIMARY = 3; 792 int IS_SUPER_PRIMARY = 4; 793 int DATA = 5; 794 int TYPE = 6; 795 int LABEL = 7; 796 int AUX_DATA = 8; 797 } 798 799 private interface PostalInsert { 800 String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" + 801 Data.RAW_CONTACT_ID + "," + 802 DataColumns.MIMETYPE_ID + "," + 803 Data.IS_PRIMARY + "," + 804 Data.IS_SUPER_PRIMARY + "," + 805 StructuredPostal.FORMATTED_ADDRESS + "," + 806 StructuredPostal.TYPE + "," + 807 StructuredPostal.LABEL + "," + 808 Data.DATA14 + 809 ") VALUES (?,?,?,?,?,?,?,?)"; 810 811 int RAW_CONTACT_ID = 1; 812 int MIMETYPE_ID = 2; 813 int IS_PRIMARY = 3; 814 int IS_SUPER_PRIMARY = 4; 815 int DATA = 5; 816 int TYPE = 6; 817 int LABEL = 7; 818 int AUX_DATA = 8; 819 } 820 821 private void importContactMethods() { 822 SQLiteStatement emailInsert = mTargetDb.compileStatement(EmailInsert.INSERT_SQL); 823 SQLiteStatement imInsert = mTargetDb.compileStatement(ImInsert.INSERT_SQL); 824 SQLiteStatement postalInsert = mTargetDb.compileStatement(PostalInsert.INSERT_SQL); 825 Cursor c = mSourceDb.query(ContactMethodsQuery.TABLE, ContactMethodsQuery.COLUMNS, null, 826 null, null, null, null); 827 try { 828 while (c.moveToNext()) { 829 int kind = c.getInt(ContactMethodsQuery.KIND); 830 switch (kind) { 831 case android.provider.Contacts.KIND_EMAIL: 832 insertEmail(c, emailInsert); 833 break; 834 835 case android.provider.Contacts.KIND_IM: 836 insertIm(c, imInsert); 837 break; 838 839 case android.provider.Contacts.KIND_POSTAL: 840 insertPostal(c, postalInsert); 841 break; 842 } 843 } 844 } finally { 845 c.close(); 846 emailInsert.close(); 847 imInsert.close(); 848 postalInsert.close(); 849 } 850 851 } 852 853 private void insertEmail(Cursor c, SQLiteStatement insert) { 854 long personId = c.getLong(ContactMethodsQuery.PERSON); 855 String email = c.getString(ContactMethodsQuery.DATA); 856 857 insert.bindLong(EmailInsert.RAW_CONTACT_ID, personId); 858 insert.bindLong(EmailInsert.MIMETYPE_ID, mEmailMimetypeId); 859 bindString(insert, EmailInsert.IS_PRIMARY, c.getString(ContactMethodsQuery.ISPRIMARY)); 860 bindString(insert, EmailInsert.IS_SUPER_PRIMARY, c.getString(ContactMethodsQuery.ISPRIMARY)); 861 bindString(insert, EmailInsert.DATA, email); 862 bindString(insert, EmailInsert.AUX_DATA, c.getString(ContactMethodsQuery.AUX_DATA)); 863 bindString(insert, EmailInsert.TYPE, c.getString(ContactMethodsQuery.TYPE)); 864 bindString(insert, EmailInsert.LABEL, c.getString(ContactMethodsQuery.LABEL)); 865 866 long dataId = insert(insert); 867 mDbHelper.insertNameLookupForEmail(personId, dataId, email); 868 } 869 870 private void insertIm(Cursor c, SQLiteStatement insert) { 871 long personId = c.getLong(ContactMethodsQuery.PERSON); 872 873 insert.bindLong(ImInsert.RAW_CONTACT_ID, personId); 874 insert.bindLong(ImInsert.MIMETYPE_ID, mImMimetypeId); 875 bindString(insert, ImInsert.IS_PRIMARY, c.getString(ContactMethodsQuery.ISPRIMARY)); 876 bindString(insert, ImInsert.IS_SUPER_PRIMARY, c.getString(ContactMethodsQuery.ISPRIMARY)); 877 bindString(insert, ImInsert.DATA, c.getString(ContactMethodsQuery.DATA)); 878 bindString(insert, ImInsert.AUX_DATA, c.getString(ContactMethodsQuery.AUX_DATA)); 879 bindString(insert, ImInsert.TYPE, c.getString(ContactMethodsQuery.TYPE)); 880 bindString(insert, ImInsert.LABEL, c.getString(ContactMethodsQuery.LABEL)); 881 insert(insert); 882 } 883 884 private void insertPostal(Cursor c, SQLiteStatement insert) { 885 long personId = c.getLong(ContactMethodsQuery.PERSON); 886 887 insert.bindLong(PostalInsert.RAW_CONTACT_ID, personId); 888 insert.bindLong(PostalInsert.MIMETYPE_ID, mPostalMimetypeId); 889 bindString(insert, PostalInsert.IS_PRIMARY, c.getString(ContactMethodsQuery.ISPRIMARY)); 890 bindString(insert, PostalInsert.IS_SUPER_PRIMARY, 891 c.getString(ContactMethodsQuery.ISPRIMARY)); 892 bindString(insert, PostalInsert.DATA, c.getString(ContactMethodsQuery.DATA)); 893 bindString(insert, PostalInsert.AUX_DATA, c.getString(ContactMethodsQuery.AUX_DATA)); 894 bindString(insert, PostalInsert.TYPE, c.getString(ContactMethodsQuery.TYPE)); 895 bindString(insert, PostalInsert.LABEL, c.getString(ContactMethodsQuery.LABEL)); 896 insert(insert); 897 } 898 899 private interface PhonesQuery { 900 String TABLE = "phones"; 901 902 String[] COLUMNS = { 903 "person", "type", "number", "label", "isprimary", 904 }; 905 906 static int PERSON = 0; 907 static int TYPE = 1; 908 static int NUMBER = 2; 909 static int LABEL = 3; 910 static int ISPRIMARY = 4; 911 } 912 913 private interface PhoneInsert { 914 String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" + 915 Data.RAW_CONTACT_ID + "," + 916 DataColumns.MIMETYPE_ID + "," + 917 Data.IS_PRIMARY + "," + 918 Data.IS_SUPER_PRIMARY + "," + 919 Phone.NUMBER + "," + 920 Phone.TYPE + "," + 921 Phone.LABEL + "," + 922 PhoneColumns.NORMALIZED_NUMBER + 923 ") VALUES (?,?,?,?,?,?,?,?)"; 924 925 int RAW_CONTACT_ID = 1; 926 int MIMETYPE_ID = 2; 927 int IS_PRIMARY = 3; 928 int IS_SUPER_PRIMARY = 4; 929 int NUMBER = 5; 930 int TYPE = 6; 931 int LABEL = 7; 932 int NORMALIZED_NUMBER = 8; 933 } 934 935 private interface PhoneLookupInsert { 936 String INSERT_SQL = "INSERT INTO " + Tables.PHONE_LOOKUP + "(" + 937 PhoneLookupColumns.RAW_CONTACT_ID + "," + 938 PhoneLookupColumns.DATA_ID + "," + 939 PhoneLookupColumns.NORMALIZED_NUMBER + "," + 940 PhoneLookupColumns.MIN_MATCH + 941 ") VALUES (?,?,?,?)"; 942 943 int RAW_CONTACT_ID = 1; 944 int DATA_ID = 2; 945 int NORMALIZED_NUMBER = 3; 946 int MIN_MATCH = 4; 947 } 948 949 private interface HasPhoneNumberUpdate { 950 String UPDATE_SQL = "UPDATE " + Tables.CONTACTS + 951 " SET " + Contacts.HAS_PHONE_NUMBER + "=1 WHERE " + Contacts._ID + "=?"; 952 953 int CONTACT_ID = 1; 954 } 955 956 private void importPhones() { 957 SQLiteStatement phoneInsert = mTargetDb.compileStatement(PhoneInsert.INSERT_SQL); 958 SQLiteStatement phoneLookupInsert = 959 mTargetDb.compileStatement(PhoneLookupInsert.INSERT_SQL); 960 SQLiteStatement hasPhoneUpdate = 961 mTargetDb.compileStatement(HasPhoneNumberUpdate.UPDATE_SQL); 962 Cursor c = mSourceDb.query(PhonesQuery.TABLE, PhonesQuery.COLUMNS, null, null, 963 null, null, null); 964 try { 965 while (c.moveToNext()) { 966 insertPhone(c, phoneInsert, phoneLookupInsert, hasPhoneUpdate); 967 } 968 } finally { 969 c.close(); 970 phoneInsert.close(); 971 phoneLookupInsert.close(); 972 hasPhoneUpdate.close(); 973 } 974 } 975 976 private void insertPhone(Cursor c, SQLiteStatement phoneInsert, 977 SQLiteStatement phoneLookupInsert, SQLiteStatement hasPhoneUpdate) { 978 long lastUpdatedContact = -1; 979 long id = c.getLong(PhonesQuery.PERSON); 980 String number = c.getString(PhonesQuery.NUMBER); 981 String normalizedNumber = null; 982 if (number != null) { 983 normalizedNumber = PhoneNumberUtils.getStrippedReversed(number); 984 } 985 phoneInsert.bindLong(PhoneInsert.RAW_CONTACT_ID, id); 986 phoneInsert.bindLong(PhoneInsert.MIMETYPE_ID, mPhoneMimetypeId); 987 bindString(phoneInsert, PhoneInsert.IS_PRIMARY, c.getString(PhonesQuery.ISPRIMARY)); 988 bindString(phoneInsert, PhoneInsert.IS_SUPER_PRIMARY, c.getString(PhonesQuery.ISPRIMARY)); 989 bindString(phoneInsert, PhoneInsert.NUMBER, number); 990 bindString(phoneInsert, PhoneInsert.TYPE, c.getString(PhonesQuery.TYPE)); 991 bindString(phoneInsert, PhoneInsert.LABEL, c.getString(PhonesQuery.LABEL)); 992 bindString(phoneInsert, PhoneInsert.NORMALIZED_NUMBER, normalizedNumber); 993 994 long dataId = insert(phoneInsert); 995 if (normalizedNumber != null) { 996 phoneLookupInsert.bindLong(PhoneLookupInsert.RAW_CONTACT_ID, id); 997 phoneLookupInsert.bindLong(PhoneLookupInsert.DATA_ID, dataId); 998 phoneLookupInsert.bindString(PhoneLookupInsert.NORMALIZED_NUMBER, normalizedNumber); 999 phoneLookupInsert.bindString(PhoneLookupInsert.MIN_MATCH, 1000 PhoneNumberUtils.toCallerIDMinMatch(number)); 1001 insert(phoneLookupInsert); 1002 1003 if (lastUpdatedContact != id) { 1004 lastUpdatedContact = id; 1005 hasPhoneUpdate.bindLong(HasPhoneNumberUpdate.CONTACT_ID, id); 1006 hasPhoneUpdate.execute(); 1007 } 1008 } 1009 } 1010 1011 private interface PhotosQuery { 1012 String TABLE = "photos"; 1013 1014 String[] COLUMNS = { 1015 "person", "data", "_sync_id", "_sync_account" 1016 }; 1017 1018 static int PERSON = 0; 1019 static int DATA = 1; 1020 static int _SYNC_ID = 2; 1021 static int _SYNC_ACCOUNT = 3; 1022 } 1023 1024 private interface PhotoInsert { 1025 String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" + 1026 Data.RAW_CONTACT_ID + "," + 1027 DataColumns.MIMETYPE_ID + "," + 1028 Photo.PHOTO + "," + 1029 Data.SYNC1 + 1030 ") VALUES (?,?,?,?)"; 1031 1032 int RAW_CONTACT_ID = 1; 1033 int MIMETYPE_ID = 2; 1034 int PHOTO = 3; 1035 int SYNC1 = 4; 1036 } 1037 1038 private interface PhotoIdUpdate { 1039 String UPDATE_SQL = "UPDATE " + Tables.CONTACTS + 1040 " SET " + Contacts.PHOTO_ID + "=? WHERE " + Contacts._ID + "=?"; 1041 1042 int PHOTO_ID = 1; 1043 int CONTACT_ID = 2; 1044 } 1045 1046 private void importPhotos() { 1047 SQLiteStatement insert = mTargetDb.compileStatement(PhotoInsert.INSERT_SQL); 1048 SQLiteStatement photoIdUpdate = mTargetDb.compileStatement(PhotoIdUpdate.UPDATE_SQL); 1049 Cursor c = mSourceDb.query(PhotosQuery.TABLE, PhotosQuery.COLUMNS, null, null, 1050 null, null, null); 1051 try { 1052 while (c.moveToNext()) { 1053 insertPhoto(c, insert, photoIdUpdate); 1054 } 1055 } finally { 1056 c.close(); 1057 insert.close(); 1058 photoIdUpdate.close(); 1059 } 1060 } 1061 1062 private void insertPhoto(Cursor c, SQLiteStatement insert, SQLiteStatement photoIdUpdate) { 1063 if (c.isNull(PhotosQuery.DATA)) { 1064 return; 1065 } 1066 1067 long personId = c.getLong(PhotosQuery.PERSON); 1068 1069 insert.bindLong(PhotoInsert.RAW_CONTACT_ID, personId); 1070 insert.bindLong(PhotoInsert.MIMETYPE_ID, mPhotoMimetypeId); 1071 insert.bindBlob(PhotoInsert.PHOTO, c.getBlob(PhotosQuery.DATA)); 1072 1073 String account = c.getString(PhotosQuery._SYNC_ACCOUNT); 1074 if (!TextUtils.isEmpty(account)) { 1075 bindString(insert, PhotoInsert.SYNC1, c.getString(PhotosQuery._SYNC_ID)); 1076 } else { 1077 insert.bindNull(PhotoInsert.SYNC1); 1078 } 1079 1080 long rowId = insert(insert); 1081 photoIdUpdate.bindLong(PhotoIdUpdate.PHOTO_ID, rowId); 1082 photoIdUpdate.bindLong(PhotoIdUpdate.CONTACT_ID, personId); 1083 photoIdUpdate.execute(); 1084 } 1085 1086 private interface GroupMembershipQuery { 1087 String TABLE = "groupmembership"; 1088 1089 String[] COLUMNS = { 1090 "person", "group_id", "group_sync_account", "group_sync_id" 1091 }; 1092 1093 static int PERSON_ID = 0; 1094 static int GROUP_ID = 1; 1095 static int GROUP_SYNC_ACCOUNT = 2; 1096 static int GROUP_SYNC_ID = 3; 1097 } 1098 1099 private interface GroupMembershipInsert { 1100 String INSERT_SQL = "INSERT INTO " + Tables.DATA + "(" + 1101 Data.RAW_CONTACT_ID + "," + 1102 DataColumns.MIMETYPE_ID + "," + 1103 GroupMembership.GROUP_ROW_ID + 1104 ") VALUES (?,?,?)"; 1105 1106 int RAW_CONTACT_ID = 1; 1107 int MIMETYPE_ID = 2; 1108 int GROUP_ROW_ID = 3; 1109 } 1110 1111 private void importGroupMemberships() { 1112 SQLiteStatement insert = mTargetDb.compileStatement(GroupMembershipInsert.INSERT_SQL); 1113 Cursor c = mSourceDb.query(GroupMembershipQuery.TABLE, GroupMembershipQuery.COLUMNS, null, 1114 null, null, null, null); 1115 try { 1116 while (c.moveToNext()) { 1117 insertGroupMembership(c, insert); 1118 } 1119 } finally { 1120 c.close(); 1121 insert.close(); 1122 } 1123 } 1124 1125 private void insertGroupMembership(Cursor c, SQLiteStatement insert) { 1126 long personId = c.getLong(GroupMembershipQuery.PERSON_ID); 1127 1128 long groupId = 0; 1129 if (c.isNull(GroupMembershipQuery.GROUP_ID)) { 1130 String account = c.getString(GroupMembershipQuery.GROUP_SYNC_ACCOUNT); 1131 if (!TextUtils.isEmpty(account)) { 1132 String syncId = c.getString(GroupMembershipQuery.GROUP_SYNC_ID); 1133 1134 Cursor cursor = mTargetDb.query(Tables.GROUPS, 1135 new String[]{Groups._ID}, Groups.SOURCE_ID + "=?", new String[]{syncId}, 1136 null, null, null); 1137 try { 1138 if (cursor.moveToFirst()) { 1139 groupId = cursor.getLong(0); 1140 } 1141 } finally { 1142 cursor.close(); 1143 } 1144 1145 if (groupId == 0) { 1146 ContentValues values = new ContentValues(); 1147 values.put(Groups.ACCOUNT_NAME, account); 1148 values.put(Groups.ACCOUNT_TYPE, DEFAULT_ACCOUNT_TYPE); 1149 values.put(Groups.GROUP_VISIBLE, true); 1150 values.put(Groups.SOURCE_ID, syncId); 1151 groupId = mTargetDb.insert(Tables.GROUPS, null, values); 1152 } 1153 } 1154 } else { 1155 groupId = c.getLong(GroupMembershipQuery.GROUP_ID); 1156 } 1157 1158 insert.bindLong(GroupMembershipInsert.RAW_CONTACT_ID, personId); 1159 insert.bindLong(GroupMembershipInsert.MIMETYPE_ID, mGroupMembershipMimetypeId); 1160 insert.bindLong(GroupMembershipInsert.GROUP_ROW_ID, groupId); 1161 insert(insert); 1162 } 1163 1164 private interface CallsQuery { 1165 String TABLE = "calls"; 1166 1167 String[] COLUMNS = { 1168 "_id", "number", "date", "duration", "type", "new", "name", "numbertype", 1169 "numberlabel" 1170 }; 1171 1172 static int ID = 0; 1173 static int NUMBER = 1; 1174 static int DATE = 2; 1175 static int DURATION = 3; 1176 static int TYPE = 4; 1177 static int NEW = 5; 1178 static int NAME = 6; 1179 static int NUMBER_TYPE = 7; 1180 static int NUMBER_LABEL = 8; 1181 } 1182 1183 private void importCalls() { 1184 Cursor c = mSourceDb.query(CallsQuery.TABLE, CallsQuery.COLUMNS, null, null, 1185 null, null, null); 1186 try { 1187 while (c.moveToNext()) { 1188 insertCall(c); 1189 } 1190 } finally { 1191 c.close(); 1192 } 1193 } 1194 1195 private void insertCall(Cursor c) { 1196 1197 // Cannot use batch operations here, because call log is serviced by a separate provider 1198 mValues.clear(); 1199 mValues.put(Calls._ID, c.getLong(CallsQuery.ID)); 1200 mValues.put(Calls.NUMBER, c.getString(CallsQuery.NUMBER)); 1201 mValues.put(Calls.DATE, c.getLong(CallsQuery.DATE)); 1202 mValues.put(Calls.DURATION, c.getLong(CallsQuery.DURATION)); 1203 mValues.put(Calls.NEW, c.getLong(CallsQuery.NEW)); 1204 mValues.put(Calls.TYPE, c.getLong(CallsQuery.TYPE)); 1205 mValues.put(Calls.CACHED_NAME, c.getString(CallsQuery.NAME)); 1206 mValues.put(Calls.CACHED_NUMBER_LABEL, c.getString(CallsQuery.NUMBER_LABEL)); 1207 mValues.put(Calls.CACHED_NUMBER_TYPE, c.getString(CallsQuery.NUMBER_TYPE)); 1208 1209 // TODO: confirm that we can use the CallLogProvider at this point, that it is guaranteed 1210 // to have been registered. 1211 mResolver.insert(Calls.CONTENT_URI, mValues); 1212 } 1213 1214 private void updateDisplayNamesAndLookupKeys() { 1215 // Compute display names, sort keys, lookup key, etc. for all Raw Cont 1216 Cursor cursor = mResolver.query(RawContacts.CONTENT_URI, 1217 new String[] { RawContacts._ID }, null, null, null); 1218 try { 1219 while (cursor.moveToNext()) { 1220 long rawContactId = cursor.getLong(0); 1221 mDbHelper.updateRawContactDisplayName(mTargetDb, rawContactId); 1222 mContactsProvider.updateLookupKeyForRawContact(mTargetDb, rawContactId); 1223 } 1224 } finally { 1225 cursor.close(); 1226 } 1227 } 1228 1229 private interface DeletedPeopleQuery { 1230 String TABLE = "_deleted_people"; 1231 1232 String[] COLUMNS = { 1233 "_sync_id", "_sync_account" 1234 }; 1235 1236 static int _SYNC_ID = 0; 1237 static int _SYNC_ACCOUNT = 1; 1238 } 1239 1240 private interface DeletedRawContactInsert { 1241 String INSERT_SQL = "INSERT INTO " + Tables.RAW_CONTACTS + "(" + 1242 RawContacts.ACCOUNT_NAME + "," + 1243 RawContacts.ACCOUNT_TYPE + "," + 1244 RawContacts.SOURCE_ID + "," + 1245 RawContacts.DELETED + "," + 1246 RawContacts.AGGREGATION_MODE + 1247 ") VALUES (?,?,?,?,?)"; 1248 1249 1250 int ACCOUNT_NAME = 1; 1251 int ACCOUNT_TYPE = 2; 1252 int SOURCE_ID = 3; 1253 int DELETED = 4; 1254 int AGGREGATION_MODE = 5; 1255 } 1256 1257 private void importDeletedPeople() { 1258 SQLiteStatement insert = mTargetDb.compileStatement(DeletedRawContactInsert.INSERT_SQL); 1259 Cursor c = mSourceDb.query(DeletedPeopleQuery.TABLE, DeletedPeopleQuery.COLUMNS, null, null, 1260 null, null, null); 1261 try { 1262 while (c.moveToNext()) { 1263 insertDeletedPerson(c, insert); 1264 } 1265 } finally { 1266 c.close(); 1267 insert.close(); 1268 } 1269 } 1270 1271 private void insertDeletedPerson(Cursor c, SQLiteStatement insert) { 1272 String account = c.getString(DeletedPeopleQuery._SYNC_ACCOUNT); 1273 if (account == null) { 1274 return; 1275 } 1276 1277 insert.bindString(DeletedRawContactInsert.ACCOUNT_NAME, account); 1278 insert.bindString(DeletedRawContactInsert.ACCOUNT_TYPE, DEFAULT_ACCOUNT_TYPE); 1279 bindString(insert, DeletedRawContactInsert.SOURCE_ID, 1280 c.getString(DeletedPeopleQuery._SYNC_ID)); 1281 insert.bindLong(DeletedRawContactInsert.DELETED, 1); 1282 insert.bindLong(DeletedRawContactInsert.AGGREGATION_MODE, 1283 RawContacts.AGGREGATION_MODE_DISABLED); 1284 insert(insert); 1285 } 1286 1287 private void bindString(SQLiteStatement insert, int index, String string) { 1288 if (string == null) { 1289 insert.bindNull(index); 1290 } else { 1291 insert.bindString(index, string); 1292 } 1293 } 1294 1295 private long insert(SQLiteStatement insertStatement) { 1296 long rowId = insertStatement.executeInsert(); 1297 if (rowId == 0) { 1298 throw new RuntimeException("Insert failed"); 1299 } 1300 1301 mBatchCounter++; 1302 if (mBatchCounter >= INSERT_BATCH_SIZE) { 1303 mTargetDb.setTransactionSuccessful(); 1304 mTargetDb.endTransaction(); 1305 mTargetDb.beginTransaction(); 1306 mBatchCounter = 0; 1307 } 1308 return rowId; 1309 } 1310 } 1311