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