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 static com.android.providers.contacts.ContactsActor.PACKAGE_GREY; 20 21 import com.google.android.collect.Sets; 22 23 import android.accounts.Account; 24 import android.content.ContentProvider; 25 import android.content.ContentResolver; 26 import android.content.ContentUris; 27 import android.content.ContentValues; 28 import android.content.Context; 29 import android.content.Entity; 30 import android.database.Cursor; 31 import android.net.Uri; 32 import android.os.Bundle; 33 import android.provider.ContactsContract; 34 import android.provider.ContactsContract.AggregationExceptions; 35 import android.provider.ContactsContract.CommonDataKinds.Email; 36 import android.provider.ContactsContract.CommonDataKinds.Event; 37 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 38 import android.provider.ContactsContract.CommonDataKinds.Im; 39 import android.provider.ContactsContract.CommonDataKinds.Nickname; 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.SearchSnippetColumns; 51 import android.provider.ContactsContract.Settings; 52 import android.provider.ContactsContract.StatusUpdates; 53 import android.provider.ContactsContract.StreamItems; 54 import android.test.MoreAsserts; 55 import android.test.mock.MockContentResolver; 56 import android.util.Log; 57 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.BitSet; 61 import java.util.Comparator; 62 import java.util.Iterator; 63 import java.util.Map; 64 import java.util.Map.Entry; 65 import java.util.Set; 66 67 /** 68 * A common superclass for {@link ContactsProvider2}-related tests. 69 */ 70 public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase { 71 72 protected static final String PACKAGE = "ContactsProvider2Test"; 73 public static final String READ_ONLY_ACCOUNT_TYPE = 74 SynchronousContactsProvider2.READ_ONLY_ACCOUNT_TYPE; 75 76 protected ContactsActor mActor; 77 protected MockContentResolver mResolver; 78 protected Account mAccount = new Account("account1", "account type1"); 79 protected Account mAccountTwo = new Account("account2", "account type2"); 80 81 protected final static Long NO_LONG = new Long(0); 82 protected final static String NO_STRING = new String(""); 83 protected final static Account NO_ACCOUNT = new Account("a", "b"); 84 85 protected Class<? extends ContentProvider> getProviderClass() { 86 return SynchronousContactsProvider2.class; 87 } 88 89 protected String getAuthority() { 90 return ContactsContract.AUTHORITY; 91 } 92 93 @Override 94 protected void setUp() throws Exception { 95 super.setUp(); 96 97 mActor = new ContactsActor(getContext(), PACKAGE_GREY, getProviderClass(), getAuthority()); 98 mResolver = mActor.resolver; 99 if (mActor.provider instanceof SynchronousContactsProvider2) { 100 getContactsProvider().wipeData(); 101 } 102 103 // Give the actor access to read/write contacts and profile data by default. 104 mActor.addPermissions( 105 "android.permission.READ_CONTACTS", 106 "android.permission.WRITE_CONTACTS", 107 "android.permission.READ_SOCIAL_STREAM", 108 "android.permission.WRITE_SOCIAL_STREAM", 109 "android.permission.READ_PROFILE", 110 "android.permission.WRITE_PROFILE"); 111 } 112 113 @Override 114 protected void tearDown() throws Exception { 115 super.tearDown(); 116 } 117 118 public SynchronousContactsProvider2 getContactsProvider() { 119 return (SynchronousContactsProvider2) mActor.provider; 120 } 121 122 public Context getMockContext() { 123 return mActor.context; 124 } 125 126 public void addAuthority(String authority) { 127 mActor.addAuthority(authority); 128 } 129 130 public ContentProvider addProvider(Class<? extends ContentProvider> providerClass, 131 String authority) throws Exception { 132 return mActor.addProvider(providerClass, authority); 133 } 134 135 public ContentProvider getProvider() { 136 return mActor.provider; 137 } 138 139 protected Uri maybeAddAccountQueryParameters(Uri uri, Account account) { 140 if (account == null) { 141 return uri; 142 } 143 return uri.buildUpon() 144 .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name) 145 .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type) 146 .build(); 147 } 148 149 protected long createRawContact() { 150 return createRawContact(null); 151 } 152 153 protected long createRawContactWithName() { 154 return createRawContactWithName(null); 155 } 156 157 protected long createRawContactWithName(Account account) { 158 return createRawContactWithName("John", "Doe", account); 159 } 160 161 protected long createRawContactWithName(String firstName, String lastName) { 162 return createRawContactWithName(firstName, lastName, null); 163 } 164 165 protected long createRawContactWithName(String firstName, String lastName, Account account) { 166 long rawContactId = createRawContact(account); 167 insertStructuredName(rawContactId, firstName, lastName); 168 return rawContactId; 169 } 170 171 protected Uri setCallerIsSyncAdapter(Uri uri, Account account) { 172 if (account == null) { 173 return uri; 174 } 175 final Uri.Builder builder = uri.buildUpon(); 176 builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, account.name); 177 builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type); 178 builder.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true"); 179 return builder.build(); 180 } 181 182 protected long createRawContact(Account account, String... extras) { 183 ContentValues values = new ContentValues(); 184 extrasVarArgsToValues(values, extras); 185 final Uri uri = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account); 186 Uri contactUri = mResolver.insert(uri, values); 187 return ContentUris.parseId(contactUri); 188 } 189 190 protected int updateItem(Uri uri, long id, String... extras) { 191 Uri itemUri = ContentUris.withAppendedId(uri, id); 192 return updateItem(itemUri, extras); 193 } 194 195 protected int updateItem(Uri uri, String... extras) { 196 ContentValues values = new ContentValues(); 197 extrasVarArgsToValues(values, extras); 198 return mResolver.update(uri, values, null, null); 199 } 200 201 private static void extrasVarArgsToValues(ContentValues values, String... extras) { 202 for (int i = 0; i < extras.length; ) { 203 values.put(extras[i], extras[i + 1]); 204 i += 2; 205 } 206 } 207 208 protected long createGroup(Account account, String sourceId, String title) { 209 return createGroup(account, sourceId, title, 1, false, false); 210 } 211 212 protected long createGroup(Account account, String sourceId, String title, int visible) { 213 return createGroup(account, sourceId, title, visible, false, false); 214 } 215 216 protected long createAutoAddGroup(Account account) { 217 return createGroup(account, "auto", "auto", 218 0 /* visible */, true /* auto-add */, false /* fav */); 219 } 220 221 protected long createGroup(Account account, String sourceId, String title, 222 int visible, boolean autoAdd, boolean favorite) { 223 ContentValues values = new ContentValues(); 224 values.put(Groups.SOURCE_ID, sourceId); 225 values.put(Groups.TITLE, title); 226 values.put(Groups.GROUP_VISIBLE, visible); 227 values.put(Groups.AUTO_ADD, autoAdd ? 1 : 0); 228 values.put(Groups.FAVORITES, favorite ? 1 : 0); 229 final Uri uri = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account); 230 return ContentUris.parseId(mResolver.insert(uri, values)); 231 } 232 233 protected void createSettings(Account account, String shouldSync, String ungroupedVisible) { 234 createSettings(new AccountWithDataSet(account.name, account.type, null), 235 shouldSync, ungroupedVisible); 236 } 237 238 protected void createSettings(AccountWithDataSet account, String shouldSync, 239 String ungroupedVisible) { 240 ContentValues values = new ContentValues(); 241 values.put(Settings.ACCOUNT_NAME, account.getAccountName()); 242 values.put(Settings.ACCOUNT_TYPE, account.getAccountType()); 243 if (account.getDataSet() != null) { 244 values.put(Settings.DATA_SET, account.getDataSet()); 245 } 246 values.put(Settings.SHOULD_SYNC, shouldSync); 247 values.put(Settings.UNGROUPED_VISIBLE, ungroupedVisible); 248 mResolver.insert(Settings.CONTENT_URI, values); 249 } 250 251 protected Uri insertStructuredName(long rawContactId, String givenName, String familyName) { 252 ContentValues values = new ContentValues(); 253 StringBuilder sb = new StringBuilder(); 254 if (givenName != null) { 255 sb.append(givenName); 256 } 257 if (givenName != null && familyName != null) { 258 sb.append(" "); 259 } 260 if (familyName != null) { 261 sb.append(familyName); 262 } 263 values.put(StructuredName.DISPLAY_NAME, sb.toString()); 264 values.put(StructuredName.GIVEN_NAME, givenName); 265 values.put(StructuredName.FAMILY_NAME, familyName); 266 267 return insertStructuredName(rawContactId, values); 268 } 269 270 protected Uri insertStructuredName(long rawContactId, ContentValues values) { 271 values.put(Data.RAW_CONTACT_ID, rawContactId); 272 values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 273 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 274 return resultUri; 275 } 276 277 protected Uri insertOrganization(long rawContactId, ContentValues values) { 278 return insertOrganization(rawContactId, values, false); 279 } 280 281 protected Uri insertOrganization(long rawContactId, ContentValues values, boolean primary) { 282 values.put(Data.RAW_CONTACT_ID, rawContactId); 283 values.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); 284 values.put(Organization.TYPE, Organization.TYPE_WORK); 285 if (primary) { 286 values.put(Data.IS_PRIMARY, 1); 287 } 288 289 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 290 return resultUri; 291 } 292 293 protected Uri insertPhoneNumber(long rawContactId, String phoneNumber) { 294 return insertPhoneNumber(rawContactId, phoneNumber, false); 295 } 296 297 protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary) { 298 return insertPhoneNumber(rawContactId, phoneNumber, primary, Phone.TYPE_HOME); 299 } 300 301 protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary, 302 int type) { 303 ContentValues values = new ContentValues(); 304 values.put(Data.RAW_CONTACT_ID, rawContactId); 305 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 306 values.put(Phone.NUMBER, phoneNumber); 307 values.put(Phone.TYPE, type); 308 if (primary) { 309 values.put(Data.IS_PRIMARY, 1); 310 } 311 312 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 313 return resultUri; 314 } 315 316 protected Uri insertEmail(long rawContactId, String email) { 317 return insertEmail(rawContactId, email, false); 318 } 319 320 protected Uri insertEmail(long rawContactId, String email, boolean primary) { 321 return insertEmail(rawContactId, email, primary, Email.TYPE_HOME, null); 322 } 323 324 protected Uri insertEmail(long rawContactId, String email, boolean primary, int type, 325 String label) { 326 ContentValues values = new ContentValues(); 327 values.put(Data.RAW_CONTACT_ID, rawContactId); 328 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 329 values.put(Email.DATA, email); 330 values.put(Email.TYPE, type); 331 values.put(Email.LABEL, label); 332 if (primary) { 333 values.put(Data.IS_PRIMARY, 1); 334 } 335 336 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 337 return resultUri; 338 } 339 340 protected Uri insertNickname(long rawContactId, String nickname) { 341 ContentValues values = new ContentValues(); 342 values.put(Data.RAW_CONTACT_ID, rawContactId); 343 values.put(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE); 344 values.put(Nickname.NAME, nickname); 345 values.put(Nickname.TYPE, Nickname.TYPE_OTHER_NAME); 346 347 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 348 return resultUri; 349 } 350 351 protected Uri insertPostalAddress(long rawContactId, String formattedAddress) { 352 ContentValues values = new ContentValues(); 353 values.put(Data.RAW_CONTACT_ID, rawContactId); 354 values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 355 values.put(StructuredPostal.FORMATTED_ADDRESS, formattedAddress); 356 357 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 358 return resultUri; 359 } 360 361 protected Uri insertPostalAddress(long rawContactId, ContentValues values) { 362 values.put(Data.RAW_CONTACT_ID, rawContactId); 363 values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 364 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 365 return resultUri; 366 } 367 368 protected Uri insertPhoto(long rawContactId) { 369 ContentValues values = new ContentValues(); 370 values.put(Data.RAW_CONTACT_ID, rawContactId); 371 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 372 values.put(Photo.PHOTO, loadTestPhoto()); 373 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 374 return resultUri; 375 } 376 377 protected Uri insertPhoto(long rawContactId, int resourceId) { 378 ContentValues values = new ContentValues(); 379 values.put(Data.RAW_CONTACT_ID, rawContactId); 380 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 381 values.put(Photo.PHOTO, loadPhotoFromResource(resourceId, PhotoSize.ORIGINAL)); 382 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 383 return resultUri; 384 } 385 386 protected Uri insertGroupMembership(long rawContactId, String sourceId) { 387 ContentValues values = new ContentValues(); 388 values.put(Data.RAW_CONTACT_ID, rawContactId); 389 values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); 390 values.put(GroupMembership.GROUP_SOURCE_ID, sourceId); 391 return mResolver.insert(Data.CONTENT_URI, values); 392 } 393 394 protected Uri insertGroupMembership(long rawContactId, Long groupId) { 395 ContentValues values = new ContentValues(); 396 values.put(Data.RAW_CONTACT_ID, rawContactId); 397 values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); 398 values.put(GroupMembership.GROUP_ROW_ID, groupId); 399 return mResolver.insert(Data.CONTENT_URI, values); 400 } 401 402 public void removeGroupMemberships(long rawContactId) { 403 mResolver.delete(Data.CONTENT_URI, 404 Data.MIMETYPE + "=? AND " + GroupMembership.RAW_CONTACT_ID + "=?", 405 new String[] { GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(rawContactId) }); 406 } 407 408 protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle, 409 int presence, String status, int chatMode) { 410 return insertStatusUpdate(protocol, customProtocol, handle, presence, status, chatMode, 411 false); 412 } 413 414 protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle, 415 int presence, String status, int chatMode, boolean isUserProfile) { 416 return insertStatusUpdate(protocol, customProtocol, handle, presence, status, 0, chatMode, 417 isUserProfile); 418 } 419 420 protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle, 421 int presence, String status, long timestamp, int chatMode, boolean isUserProfile) { 422 ContentValues values = new ContentValues(); 423 values.put(StatusUpdates.PROTOCOL, protocol); 424 values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol); 425 values.put(StatusUpdates.IM_HANDLE, handle); 426 return insertStatusUpdate(values, presence, status, timestamp, chatMode, isUserProfile); 427 } 428 429 protected Uri insertStatusUpdate( 430 long dataId, int presence, String status, long timestamp, int chatMode) { 431 return insertStatusUpdate(dataId, presence, status, timestamp, chatMode, false); 432 } 433 434 protected Uri insertStatusUpdate( 435 long dataId, int presence, String status, long timestamp, int chatMode, 436 boolean isUserProfile) { 437 ContentValues values = new ContentValues(); 438 values.put(StatusUpdates.DATA_ID, dataId); 439 return insertStatusUpdate(values, presence, status, timestamp, chatMode, isUserProfile); 440 } 441 442 private Uri insertStatusUpdate( 443 ContentValues values, int presence, String status, long timestamp, int chatMode, 444 boolean isUserProfile) { 445 if (presence != 0) { 446 values.put(StatusUpdates.PRESENCE, presence); 447 values.put(StatusUpdates.CHAT_CAPABILITY, chatMode); 448 } 449 if (status != null) { 450 values.put(StatusUpdates.STATUS, status); 451 } 452 if (timestamp != 0) { 453 values.put(StatusUpdates.STATUS_TIMESTAMP, timestamp); 454 } 455 456 Uri insertUri = isUserProfile 457 ? StatusUpdates.PROFILE_CONTENT_URI 458 : StatusUpdates.CONTENT_URI; 459 Uri resultUri = mResolver.insert(insertUri, values); 460 return resultUri; 461 } 462 463 protected Uri insertStreamItem(long rawContactId, ContentValues values, Account account) { 464 return mResolver.insert( 465 maybeAddAccountQueryParameters( 466 Uri.withAppendedPath( 467 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 468 RawContacts.StreamItems.CONTENT_DIRECTORY), 469 account), 470 values); 471 } 472 473 protected Uri insertStreamItemPhoto(long streamItemId, ContentValues values, Account account) { 474 return mResolver.insert( 475 maybeAddAccountQueryParameters( 476 Uri.withAppendedPath( 477 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), 478 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), 479 account), 480 values); 481 } 482 483 protected Uri insertImHandle(long rawContactId, int protocol, String customProtocol, 484 String handle) { 485 ContentValues values = new ContentValues(); 486 values.put(Data.RAW_CONTACT_ID, rawContactId); 487 values.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); 488 values.put(Im.PROTOCOL, protocol); 489 values.put(Im.CUSTOM_PROTOCOL, customProtocol); 490 values.put(Im.DATA, handle); 491 values.put(Im.TYPE, Im.TYPE_HOME); 492 493 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 494 return resultUri; 495 } 496 497 protected Uri insertEvent(long rawContactId, int type, String date) { 498 ContentValues values = new ContentValues(); 499 values.put(Data.RAW_CONTACT_ID, rawContactId); 500 values.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); 501 values.put(Event.TYPE, type); 502 values.put(Event.START_DATE, date); 503 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 504 return resultUri; 505 } 506 507 protected Uri insertNote(long rawContactId, String note) { 508 ContentValues values = new ContentValues(); 509 values.put(Data.RAW_CONTACT_ID, rawContactId); 510 values.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE); 511 values.put(Note.NOTE, note); 512 Uri resultUri = mResolver.insert(Data.CONTENT_URI, values); 513 return resultUri; 514 } 515 516 protected void setContactAccount(long rawContactId, String accountType, String accountName) { 517 ContentValues values = new ContentValues(); 518 values.put(RawContacts.ACCOUNT_TYPE, accountType); 519 values.put(RawContacts.ACCOUNT_NAME, accountName); 520 521 mResolver.update(ContentUris.withAppendedId( 522 RawContacts.CONTENT_URI, rawContactId), values, null, null); 523 } 524 525 protected void setAggregationException(int type, long rawContactId1, long rawContactId2) { 526 ContentValues values = new ContentValues(); 527 values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1); 528 values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2); 529 values.put(AggregationExceptions.TYPE, type); 530 assertEquals(1, mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null)); 531 } 532 533 protected Cursor queryRawContact(long rawContactId) { 534 return mResolver.query(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 535 null, null, null, null); 536 } 537 538 protected Cursor queryContact(long contactId) { 539 return mResolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 540 null, null, null, null); 541 } 542 543 protected Cursor queryContact(long contactId, String[] projection) { 544 return mResolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 545 projection, null, null, null); 546 } 547 548 protected Uri getContactUriForRawContact(long rawContactId) { 549 return ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId)); 550 } 551 552 protected long queryContactId(long rawContactId) { 553 Cursor c = queryRawContact(rawContactId); 554 assertTrue(c.moveToFirst()); 555 long contactId = c.getLong(c.getColumnIndex(RawContacts.CONTACT_ID)); 556 c.close(); 557 return contactId; 558 } 559 560 protected long queryPhotoId(long contactId) { 561 Cursor c = queryContact(contactId); 562 assertTrue(c.moveToFirst()); 563 long photoId = c.getInt(c.getColumnIndex(Contacts.PHOTO_ID)); 564 c.close(); 565 return photoId; 566 } 567 568 protected long queryPhotoFileId(long contactId) { 569 return getStoredLongValue(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), 570 Contacts.PHOTO_FILE_ID); 571 } 572 573 protected boolean queryRawContactIsStarred(long rawContactId) { 574 Cursor c = queryRawContact(rawContactId); 575 try { 576 assertTrue(c.moveToFirst()); 577 return c.getLong(c.getColumnIndex(RawContacts.STARRED)) != 0; 578 } finally { 579 c.close(); 580 } 581 } 582 583 protected String queryDisplayName(long contactId) { 584 Cursor c = queryContact(contactId); 585 assertTrue(c.moveToFirst()); 586 String displayName = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME)); 587 c.close(); 588 return displayName; 589 } 590 591 protected String queryLookupKey(long contactId) { 592 Cursor c = queryContact(contactId); 593 assertTrue(c.moveToFirst()); 594 String lookupKey = c.getString(c.getColumnIndex(Contacts.LOOKUP_KEY)); 595 c.close(); 596 return lookupKey; 597 } 598 599 protected void assertAggregated(long rawContactId1, long rawContactId2) { 600 long contactId1 = queryContactId(rawContactId1); 601 long contactId2 = queryContactId(rawContactId2); 602 assertTrue(contactId1 == contactId2); 603 } 604 605 protected void assertAggregated(long rawContactId1, long rawContactId2, 606 String expectedDisplayName) { 607 long contactId1 = queryContactId(rawContactId1); 608 long contactId2 = queryContactId(rawContactId2); 609 assertTrue(contactId1 == contactId2); 610 611 String displayName = queryDisplayName(contactId1); 612 assertEquals(expectedDisplayName, displayName); 613 } 614 615 protected void assertNotAggregated(long rawContactId1, long rawContactId2) { 616 long contactId1 = queryContactId(rawContactId1); 617 long contactId2 = queryContactId(rawContactId2); 618 assertTrue(contactId1 != contactId2); 619 } 620 621 protected void assertStructuredName(long rawContactId, String prefix, String givenName, 622 String middleName, String familyName, String suffix) { 623 Uri uri = Uri.withAppendedPath( 624 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 625 RawContacts.Data.CONTENT_DIRECTORY); 626 627 final String[] projection = new String[] { 628 StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME, 629 StructuredName.FAMILY_NAME, StructuredName.SUFFIX 630 }; 631 632 Cursor c = mResolver.query(uri, projection, Data.MIMETYPE + "='" 633 + StructuredName.CONTENT_ITEM_TYPE + "'", null, null); 634 635 assertTrue(c.moveToFirst()); 636 assertEquals(prefix, c.getString(0)); 637 assertEquals(givenName, c.getString(1)); 638 assertEquals(middleName, c.getString(2)); 639 assertEquals(familyName, c.getString(3)); 640 assertEquals(suffix, c.getString(4)); 641 c.close(); 642 } 643 644 protected long assertSingleGroup(Long rowId, Account account, String sourceId, String title) { 645 Cursor c = mResolver.query(Groups.CONTENT_URI, null, null, null, null); 646 try { 647 assertTrue(c.moveToNext()); 648 long actualRowId = assertGroup(c, rowId, account, sourceId, title); 649 assertFalse(c.moveToNext()); 650 return actualRowId; 651 } finally { 652 c.close(); 653 } 654 } 655 656 protected long assertSingleGroupMembership(Long rowId, Long rawContactId, Long groupRowId, 657 String sourceId) { 658 Cursor c = mResolver.query(ContactsContract.Data.CONTENT_URI, null, null, null, null); 659 try { 660 assertTrue(c.moveToNext()); 661 long actualRowId = assertGroupMembership(c, rowId, rawContactId, groupRowId, sourceId); 662 assertFalse(c.moveToNext()); 663 return actualRowId; 664 } finally { 665 c.close(); 666 } 667 } 668 669 protected long assertGroupMembership(Cursor c, Long rowId, Long rawContactId, Long groupRowId, 670 String sourceId) { 671 assertNullOrEquals(c, rowId, Data._ID); 672 assertNullOrEquals(c, rawContactId, GroupMembership.RAW_CONTACT_ID); 673 assertNullOrEquals(c, groupRowId, GroupMembership.GROUP_ROW_ID); 674 assertNullOrEquals(c, sourceId, GroupMembership.GROUP_SOURCE_ID); 675 return c.getLong(c.getColumnIndexOrThrow("_id")); 676 } 677 678 protected long assertGroup(Cursor c, Long rowId, Account account, String sourceId, String title) { 679 assertNullOrEquals(c, rowId, Groups._ID); 680 assertNullOrEquals(c, account); 681 assertNullOrEquals(c, sourceId, Groups.SOURCE_ID); 682 assertNullOrEquals(c, title, Groups.TITLE); 683 return c.getLong(c.getColumnIndexOrThrow("_id")); 684 } 685 686 private void assertNullOrEquals(Cursor c, Account account) { 687 if (account == NO_ACCOUNT) { 688 return; 689 } 690 if (account == null) { 691 assertTrue(c.isNull(c.getColumnIndexOrThrow(Groups.ACCOUNT_NAME))); 692 assertTrue(c.isNull(c.getColumnIndexOrThrow(Groups.ACCOUNT_TYPE))); 693 } else { 694 assertEquals(account.name, c.getString(c.getColumnIndexOrThrow(Groups.ACCOUNT_NAME))); 695 assertEquals(account.type, c.getString(c.getColumnIndexOrThrow(Groups.ACCOUNT_TYPE))); 696 } 697 } 698 699 private void assertNullOrEquals(Cursor c, Long value, String columnName) { 700 if (value != NO_LONG) { 701 if (value == null) assertTrue(c.isNull(c.getColumnIndexOrThrow(columnName))); 702 else assertEquals((long) value, c.getLong(c.getColumnIndexOrThrow(columnName))); 703 } 704 } 705 706 private void assertNullOrEquals(Cursor c, String value, String columnName) { 707 if (value != NO_STRING) { 708 if (value == null) assertTrue(c.isNull(c.getColumnIndexOrThrow(columnName))); 709 else assertEquals(value, c.getString(c.getColumnIndexOrThrow(columnName))); 710 } 711 } 712 713 protected void assertDataRow(ContentValues actual, String expectedMimetype, 714 Object... expectedArguments) { 715 assertEquals(actual.toString(), expectedMimetype, actual.getAsString(Data.MIMETYPE)); 716 for (int i = 0; i < expectedArguments.length; i += 2) { 717 String columnName = (String) expectedArguments[i]; 718 Object expectedValue = expectedArguments[i + 1]; 719 if (expectedValue instanceof Uri) { 720 expectedValue = ContentUris.parseId((Uri) expectedValue); 721 } 722 if (expectedValue == null) { 723 assertNull(actual.toString(), actual.get(columnName)); 724 } 725 if (expectedValue instanceof Long) { 726 assertEquals("mismatch at " + columnName + " from " + actual.toString(), 727 expectedValue, actual.getAsLong(columnName)); 728 } else if (expectedValue instanceof Integer) { 729 assertEquals("mismatch at " + columnName + " from " + actual.toString(), 730 expectedValue, actual.getAsInteger(columnName)); 731 } else if (expectedValue instanceof String) { 732 assertEquals("mismatch at " + columnName + " from " + actual.toString(), 733 expectedValue, actual.getAsString(columnName)); 734 } else { 735 assertEquals("mismatch at " + columnName + " from " + actual.toString(), 736 expectedValue, actual.get(columnName)); 737 } 738 } 739 } 740 741 protected void assertNoRowsAndClose(Cursor c) { 742 try { 743 assertFalse(c.moveToNext()); 744 } finally { 745 c.close(); 746 } 747 } 748 749 protected static class IdComparator implements Comparator<ContentValues> { 750 @Override 751 public int compare(ContentValues o1, ContentValues o2) { 752 long id1 = o1.getAsLong(ContactsContract.Data._ID); 753 long id2 = o2.getAsLong(ContactsContract.Data._ID); 754 if (id1 == id2) return 0; 755 return (id1 < id2) ? -1 : 1; 756 } 757 } 758 759 protected ContentValues[] asSortedContentValuesArray( 760 ArrayList<Entity.NamedContentValues> subValues) { 761 ContentValues[] result = new ContentValues[subValues.size()]; 762 int i = 0; 763 for (Entity.NamedContentValues subValue : subValues) { 764 result[i] = subValue.values; 765 i++; 766 } 767 Arrays.sort(result, new IdComparator()); 768 return result; 769 } 770 771 protected void assertDirty(Uri uri, boolean state) { 772 Cursor c = mResolver.query(uri, new String[]{"dirty"}, null, null, null); 773 assertTrue(c.moveToNext()); 774 assertEquals(state, c.getLong(0) != 0); 775 assertFalse(c.moveToNext()); 776 c.close(); 777 } 778 779 protected long getVersion(Uri uri) { 780 Cursor c = mResolver.query(uri, new String[]{"version"}, null, null, null); 781 assertTrue(c.moveToNext()); 782 long version = c.getLong(0); 783 assertFalse(c.moveToNext()); 784 c.close(); 785 return version; 786 } 787 788 protected void clearDirty(Uri uri) { 789 ContentValues values = new ContentValues(); 790 values.put("dirty", 0); 791 mResolver.update(uri, values, null, null); 792 } 793 794 protected void storeValue(Uri contentUri, long id, String column, String value) { 795 storeValue(ContentUris.withAppendedId(contentUri, id), column, value); 796 } 797 798 protected void storeValue(Uri contentUri, String column, String value) { 799 ContentValues values = new ContentValues(); 800 values.put(column, value); 801 802 mResolver.update(contentUri, values, null, null); 803 } 804 805 protected void storeValue(Uri contentUri, long id, String column, long value) { 806 storeValue(ContentUris.withAppendedId(contentUri, id), column, value); 807 } 808 809 protected void storeValue(Uri contentUri, String column, long value) { 810 ContentValues values = new ContentValues(); 811 values.put(column, value); 812 813 mResolver.update(contentUri, values, null, null); 814 } 815 816 protected void assertStoredValue(Uri contentUri, long id, String column, Object expectedValue) { 817 assertStoredValue(ContentUris.withAppendedId(contentUri, id), column, expectedValue); 818 } 819 820 protected void assertStoredValue(Uri rowUri, String column, Object expectedValue) { 821 String value = getStoredValue(rowUri, column); 822 if (expectedValue == null) { 823 assertNull("Column value " + column, value); 824 } else { 825 assertEquals("Column value " + column, String.valueOf(expectedValue), value); 826 } 827 } 828 829 protected void assertStoredValue(Uri rowUri, String selection, String[] selectionArgs, 830 String column, Object expectedValue) { 831 String value = getStoredValue(rowUri, selection, selectionArgs, column); 832 if (expectedValue == null) { 833 assertNull("Column value " + column, value); 834 } else { 835 assertEquals("Column value " + column, String.valueOf(expectedValue), value); 836 } 837 } 838 839 protected String getStoredValue(Uri rowUri, String column) { 840 return getStoredValue(rowUri, null, null, column); 841 } 842 843 protected String getStoredValue(Uri uri, String selection, String[] selectionArgs, 844 String column) { 845 String value = null; 846 Cursor c = mResolver.query(uri, new String[] { column }, selection, selectionArgs, null); 847 try { 848 assertEquals("Record count for " + uri, 1, c.getCount()); 849 850 if (c.moveToFirst()) { 851 value = getCursorStringValue(c, column); 852 } 853 } finally { 854 c.close(); 855 } 856 return value; 857 } 858 859 /** 860 * Retrieves the string value in the given column, handling deferred snippeting if the requested 861 * column is the snippet and the cursor specifies it. 862 */ 863 protected String getCursorStringValue(Cursor c, String column) { 864 String value = c.getString(c.getColumnIndex(column)); 865 if (SearchSnippetColumns.SNIPPET.equals(column)) { 866 Bundle extras = c.getExtras(); 867 if (extras.containsKey(ContactsContract.DEFERRED_SNIPPETING)) { 868 String displayName = "No display name"; 869 int displayNameColumnIndex = c.getColumnIndex(Contacts.DISPLAY_NAME); 870 if (displayNameColumnIndex != -1) { 871 displayName = c.getString(displayNameColumnIndex); 872 } 873 String query = extras.getString(ContactsContract.DEFERRED_SNIPPETING_QUERY); 874 value = ContactsContract.snippetize(value, displayName, query, 875 '[', ']', "...", 5); 876 } 877 } 878 return value; 879 } 880 881 protected Long getStoredLongValue(Uri uri, String selection, String[] selectionArgs, 882 String column) { 883 Long value = null; 884 Cursor c = mResolver.query(uri, new String[] { column }, selection, selectionArgs, null); 885 try { 886 assertEquals("Record count", 1, c.getCount()); 887 888 if (c.moveToFirst()) { 889 value = c.getLong(c.getColumnIndex(column)); 890 } 891 } finally { 892 c.close(); 893 } 894 return value; 895 } 896 897 protected Long getStoredLongValue(Uri uri, String column) { 898 return getStoredLongValue(uri, null, null, column); 899 } 900 901 protected void assertStoredValues(Uri rowUri, ContentValues expectedValues) { 902 assertStoredValues(rowUri, null, null, expectedValues); 903 } 904 905 protected void assertStoredValues(Uri rowUri, ContentValues[] expectedValues) { 906 assertStoredValues(rowUri, null, null, expectedValues); 907 } 908 909 protected void assertStoredValues(Uri rowUri, String selection, String[] selectionArgs, 910 ContentValues expectedValues) { 911 Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null); 912 try { 913 assertEquals("Record count", 1, c.getCount()); 914 c.moveToFirst(); 915 assertCursorValues(c, expectedValues); 916 } finally { 917 c.close(); 918 } 919 } 920 921 protected void assertStoredValuesWithProjection(Uri rowUri, ContentValues expectedValues) { 922 assertStoredValuesWithProjection(rowUri, new ContentValues[] {expectedValues}); 923 } 924 925 protected void assertStoredValuesWithProjection(Uri rowUri, ContentValues[] expectedValues) { 926 assertTrue("Need at least one ContentValues for this test", expectedValues.length > 0); 927 Cursor c = mResolver.query(rowUri, buildProjection(expectedValues[0]), null, null, null); 928 try { 929 assertEquals("Record count", expectedValues.length, c.getCount()); 930 c.moveToFirst(); 931 assertCursorValues(c, expectedValues); 932 } finally { 933 c.close(); 934 } 935 } 936 937 protected void assertStoredValues( 938 Uri rowUri, String selection, String[] selectionArgs, ContentValues[] expectedValues) { 939 Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null); 940 try { 941 assertEquals("Record count", expectedValues.length, c.getCount()); 942 assertCursorValues(c, expectedValues); 943 } finally { 944 c.close(); 945 } 946 } 947 948 protected void assertStoredValuesOrderly(Uri rowUri, ContentValues[] expectedValues) { 949 assertStoredValuesOrderly(rowUri, null, null, expectedValues); 950 } 951 952 protected void assertStoredValuesOrderly(Uri rowUri, String selection, 953 String[] selectionArgs, ContentValues[] expectedValues) { 954 Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null); 955 try { 956 assertEquals("Record count", expectedValues.length, c.getCount()); 957 assertCursorValuesOrderly(c, expectedValues); 958 } finally { 959 c.close(); 960 } 961 } 962 963 /** 964 * Constructs a selection (where clause) out of all supplied values, uses it 965 * to query the provider and verifies that a single row is returned and it 966 * has the same values as requested. 967 */ 968 protected void assertSelection(Uri uri, ContentValues values, String idColumn, long id) { 969 assertSelection(uri, values, idColumn, id, null); 970 } 971 972 public void assertSelectionWithProjection(Uri uri, ContentValues values, String idColumn, 973 long id) { 974 assertSelection(uri, values, idColumn, id, buildProjection(values)); 975 } 976 977 private void assertSelection(Uri uri, ContentValues values, String idColumn, long id, 978 String[] projection) { 979 StringBuilder sb = new StringBuilder(); 980 ArrayList<String> selectionArgs = new ArrayList<String>(values.size()); 981 if (idColumn != null) { 982 sb.append(idColumn).append("=").append(id); 983 } 984 Set<Map.Entry<String, Object>> entries = values.valueSet(); 985 for (Map.Entry<String, Object> entry : entries) { 986 String column = entry.getKey(); 987 Object value = entry.getValue(); 988 if (sb.length() != 0) { 989 sb.append(" AND "); 990 } 991 sb.append(column); 992 if (value == null) { 993 sb.append(" IS NULL"); 994 } else { 995 sb.append("=?"); 996 selectionArgs.add(String.valueOf(value)); 997 } 998 } 999 1000 Cursor c = mResolver.query(uri, projection, sb.toString(), selectionArgs.toArray(new String[0]), 1001 null); 1002 try { 1003 assertEquals("Record count", 1, c.getCount()); 1004 c.moveToFirst(); 1005 assertCursorValues(c, values); 1006 } finally { 1007 c.close(); 1008 } 1009 } 1010 1011 protected void assertCursorValue(Cursor cursor, String column, Object expectedValue) { 1012 String actualValue = cursor.getString(cursor.getColumnIndex(column)); 1013 assertEquals("Column " + column, String.valueOf(expectedValue), 1014 String.valueOf(actualValue)); 1015 } 1016 1017 protected void assertCursorValues(Cursor cursor, ContentValues expectedValues) { 1018 StringBuilder message = new StringBuilder(); 1019 boolean result = equalsWithExpectedValues(cursor, expectedValues, message); 1020 assertTrue(message.toString(), result); 1021 } 1022 1023 protected void assertCursorValues(Cursor cursor, ContentValues[] expectedValues) { 1024 StringBuilder message = new StringBuilder(); 1025 1026 // In case if expectedValues contains multiple identical values, remember which cursor 1027 // rows are "consumed" to prevent multiple ContentValues from hitting the same row. 1028 final BitSet used = new BitSet(cursor.getCount()); 1029 1030 for (ContentValues v : expectedValues) { 1031 boolean found = false; 1032 cursor.moveToPosition(-1); 1033 while (cursor.moveToNext()) { 1034 final int pos = cursor.getPosition(); 1035 if (used.get(pos)) continue; 1036 found = equalsWithExpectedValues(cursor, v, message); 1037 if (found) { 1038 used.set(pos); 1039 break; 1040 } 1041 } 1042 assertTrue("Expected values can not be found " + v + message.toString(), found); 1043 } 1044 } 1045 1046 protected void assertCursorValuesOrderly(Cursor cursor, ContentValues[] expectedValues) { 1047 StringBuilder message = new StringBuilder(); 1048 cursor.moveToPosition(-1); 1049 for (ContentValues v : expectedValues) { 1050 assertTrue(cursor.moveToNext()); 1051 assertTrue("ContentValues didn't match " + v + message.toString(), 1052 equalsWithExpectedValues(cursor, v, message)); 1053 } 1054 } 1055 1056 private boolean equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues, 1057 StringBuilder msgBuffer) { 1058 Set<Map.Entry<String, Object>> entries = expectedValues.valueSet(); 1059 for (Map.Entry<String, Object> entry : entries) { 1060 String column = entry.getKey(); 1061 int index = cursor.getColumnIndex(column); 1062 if (index == -1) { 1063 msgBuffer.append("No such column: ").append(column); 1064 return false; 1065 } 1066 Object expectedValue = expectedValues.get(column); 1067 String value; 1068 if (expectedValue instanceof byte[]) { 1069 expectedValue = Hex.encodeHex((byte[])expectedValue, false); 1070 value = Hex.encodeHex(cursor.getBlob(index), false); 1071 } else { 1072 expectedValue = expectedValues.getAsString(column); 1073 value = getCursorStringValue(cursor, column); 1074 } 1075 if (expectedValue != null && !expectedValue.equals(value) || value != null 1076 && !value.equals(expectedValue)) { 1077 msgBuffer 1078 .append("Column value ") 1079 .append(column) 1080 .append(" expected <") 1081 .append(expectedValue) 1082 .append(">, but was <") 1083 .append(value) 1084 .append('>'); 1085 return false; 1086 } 1087 } 1088 return true; 1089 } 1090 1091 private String[] buildProjection(ContentValues values) { 1092 String[] projection = new String[values.size()]; 1093 Iterator<Entry<String, Object>> iter = values.valueSet().iterator(); 1094 for (int i = 0; i < projection.length; i++) { 1095 projection[i] = iter.next().getKey(); 1096 } 1097 return projection; 1098 } 1099 1100 protected int getCount(Uri uri, String selection, String[] selectionArgs) { 1101 Cursor c = mResolver.query(uri, null, selection, selectionArgs, null); 1102 try { 1103 return c.getCount(); 1104 } finally { 1105 c.close(); 1106 } 1107 } 1108 1109 public static void dump(ContentResolver resolver, boolean aggregatedOnly) { 1110 String[] projection = new String[] { 1111 Contacts._ID, 1112 Contacts.DISPLAY_NAME 1113 }; 1114 String selection = null; 1115 if (aggregatedOnly) { 1116 selection = Contacts._ID 1117 + " IN (SELECT contact_id" + 1118 " FROM raw_contacts GROUP BY contact_id HAVING count(*) > 1)"; 1119 } 1120 1121 Cursor c = resolver.query(Contacts.CONTENT_URI, projection, selection, null, 1122 Contacts.DISPLAY_NAME); 1123 while(c.moveToNext()) { 1124 long contactId = c.getLong(0); 1125 Log.i("Contact ", String.format("%5d %s", contactId, c.getString(1))); 1126 dumpRawContacts(resolver, contactId); 1127 Log.i(" ", "."); 1128 } 1129 c.close(); 1130 } 1131 1132 private static void dumpRawContacts(ContentResolver resolver, long contactId) { 1133 String[] projection = new String[] { 1134 RawContacts._ID, 1135 }; 1136 Cursor c = resolver.query(RawContacts.CONTENT_URI, projection, RawContacts.CONTACT_ID + "=" 1137 + contactId, null, null); 1138 while(c.moveToNext()) { 1139 long rawContactId = c.getLong(0); 1140 Log.i("RawContact", String.format(" %-5d", rawContactId)); 1141 dumpData(resolver, rawContactId); 1142 } 1143 c.close(); 1144 } 1145 1146 private static void dumpData(ContentResolver resolver, long rawContactId) { 1147 String[] projection = new String[] { 1148 Data.MIMETYPE, 1149 Data.DATA1, 1150 Data.DATA2, 1151 Data.DATA3, 1152 }; 1153 Cursor c = resolver.query(Data.CONTENT_URI, projection, Data.RAW_CONTACT_ID + "=" 1154 + rawContactId, null, Data.MIMETYPE); 1155 while(c.moveToNext()) { 1156 String mimetype = c.getString(0); 1157 if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) { 1158 Log.i("Photo ", ""); 1159 } else { 1160 mimetype = mimetype.substring(mimetype.indexOf('/') + 1); 1161 Log.i("Data ", String.format(" %-10s %s,%s,%s", mimetype, 1162 c.getString(1), c.getString(2), c.getString(3))); 1163 } 1164 } 1165 c.close(); 1166 } 1167 1168 protected void assertNetworkNotified(boolean expected) { 1169 assertEquals(expected, (getContactsProvider()).isNetworkNotified()); 1170 } 1171 1172 protected void assertProjection(Uri uri, String[] expectedProjection) { 1173 Cursor cursor = mResolver.query(uri, null, "0", null, null); 1174 String[] actualProjection = cursor.getColumnNames(); 1175 MoreAsserts.assertEquals("Incorrect projection for URI: " + uri, 1176 Sets.newHashSet(expectedProjection), Sets.newHashSet(actualProjection)); 1177 cursor.close(); 1178 } 1179 1180 /** 1181 * A contact in the database, and the attributes used to create it. Construct using 1182 * {@link GoldenContactBuilder#build()}. 1183 */ 1184 public final class GoldenContact { 1185 1186 private final long rawContactId; 1187 1188 private final long contactId; 1189 1190 private final String givenName; 1191 1192 private final String familyName; 1193 1194 private final String nickname; 1195 1196 private final byte[] photo; 1197 1198 private final String company; 1199 1200 private final String title; 1201 1202 private final String phone; 1203 1204 private final String email; 1205 1206 private GoldenContact(GoldenContactBuilder builder, long rawContactId, long contactId) { 1207 1208 this.rawContactId = rawContactId; 1209 this.contactId = contactId; 1210 givenName = builder.givenName; 1211 familyName = builder.familyName; 1212 nickname = builder.nickname; 1213 photo = builder.photo; 1214 company = builder.company; 1215 title = builder.title; 1216 phone = builder.phone; 1217 email = builder.email; 1218 } 1219 1220 public void delete() { 1221 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 1222 mResolver.delete(rawContactUri, null, null); 1223 } 1224 1225 /** 1226 * Returns the index of the contact in table "raw_contacts" 1227 */ 1228 public long getRawContactId() { 1229 return rawContactId; 1230 } 1231 1232 /** 1233 * Returns the index of the contact in table "contacts" 1234 */ 1235 public long getContactId() { 1236 return contactId; 1237 } 1238 1239 /** 1240 * Returns the lookup key for the contact. 1241 */ 1242 public String getLookupKey() { 1243 return queryLookupKey(contactId); 1244 } 1245 1246 /** 1247 * Returns the contact's given name. 1248 */ 1249 public String getGivenName() { 1250 return givenName; 1251 } 1252 1253 /** 1254 * Returns the contact's family name. 1255 */ 1256 public String getFamilyName() { 1257 return familyName; 1258 } 1259 1260 /** 1261 * Returns the contact's nickname. 1262 */ 1263 public String getNickname() { 1264 return nickname; 1265 } 1266 1267 /** 1268 * Return's the contact's photo 1269 */ 1270 public byte[] getPhoto() { 1271 return photo; 1272 } 1273 1274 /** 1275 * Return's the company at which the contact works. 1276 */ 1277 public String getCompany() { 1278 return company; 1279 } 1280 1281 /** 1282 * Returns the contact's job title. 1283 */ 1284 public String getTitle() { 1285 return title; 1286 } 1287 1288 /** 1289 * Returns the contact's phone number 1290 */ 1291 public String getPhone() { 1292 return phone; 1293 } 1294 1295 /** 1296 * Returns the contact's email address 1297 */ 1298 public String getEmail() { 1299 return email; 1300 } 1301 } 1302 1303 /** 1304 * Builds {@link GoldenContact} objects. Unspecified boolean objects default to false. 1305 * Unspecified String objects default to null. 1306 */ 1307 public final class GoldenContactBuilder { 1308 1309 private String givenName; 1310 1311 private String familyName; 1312 1313 private String nickname; 1314 1315 private byte[] photo; 1316 1317 private String company; 1318 1319 private String title; 1320 1321 private String phone; 1322 1323 private String email; 1324 1325 /** 1326 * The contact's given and family names. 1327 * 1328 * TODO(dplotnikov): inline, or should we require them to set both names if they set either? 1329 */ 1330 public GoldenContactBuilder name(String givenName, String familyName) { 1331 return givenName(givenName).familyName(familyName); 1332 } 1333 1334 /** 1335 * The contact's given name. 1336 */ 1337 public GoldenContactBuilder givenName(String value) { 1338 givenName = value; 1339 return this; 1340 } 1341 1342 /** 1343 * The contact's family name. 1344 */ 1345 public GoldenContactBuilder familyName(String value) { 1346 familyName = value; 1347 return this; 1348 } 1349 1350 /** 1351 * The contact's nickname. 1352 */ 1353 public GoldenContactBuilder nickname(String value) { 1354 nickname = value; 1355 return this; 1356 } 1357 1358 /** 1359 * The contact's photo. 1360 */ 1361 public GoldenContactBuilder photo(byte[] value) { 1362 photo = value; 1363 return this; 1364 } 1365 1366 /** 1367 * The company at which the contact works. 1368 */ 1369 public GoldenContactBuilder company(String value) { 1370 company = value; 1371 return this; 1372 } 1373 1374 /** 1375 * The contact's job title. 1376 */ 1377 public GoldenContactBuilder title(String value) { 1378 title = value; 1379 return this; 1380 } 1381 1382 /** 1383 * The contact's phone number. 1384 */ 1385 public GoldenContactBuilder phone(String value) { 1386 phone = value; 1387 return this; 1388 } 1389 1390 /** 1391 * The contact's email address; also sets their IM status to {@link StatusUpdates#OFFLINE} 1392 * with a presence of "Coding for Android". 1393 */ 1394 public GoldenContactBuilder email(String value) { 1395 email = value; 1396 return this; 1397 } 1398 1399 /** 1400 * Builds the {@link GoldenContact} specified by this builder. 1401 */ 1402 public GoldenContact build() { 1403 1404 final long groupId = createGroup(mAccount, "gsid1", "title1"); 1405 1406 long rawContactId = createRawContact(); 1407 insertGroupMembership(rawContactId, groupId); 1408 1409 if (givenName != null || familyName != null) { 1410 insertStructuredName(rawContactId, givenName, familyName); 1411 } 1412 if (nickname != null) { 1413 insertNickname(rawContactId, nickname); 1414 } 1415 if (photo != null) { 1416 insertPhoto(rawContactId); 1417 } 1418 if (company != null || title != null) { 1419 insertOrganization(rawContactId); 1420 } 1421 if (email != null) { 1422 insertEmail(rawContactId); 1423 } 1424 if (phone != null) { 1425 insertPhone(rawContactId); 1426 } 1427 1428 long contactId = queryContactId(rawContactId); 1429 1430 return new GoldenContact(this, rawContactId, contactId); 1431 } 1432 1433 private void insertPhoto(long rawContactId) { 1434 ContentValues values = new ContentValues(); 1435 values.put(Data.RAW_CONTACT_ID, rawContactId); 1436 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 1437 values.put(Photo.PHOTO, photo); 1438 mResolver.insert(Data.CONTENT_URI, values); 1439 } 1440 1441 private void insertOrganization(long rawContactId) { 1442 1443 ContentValues values = new ContentValues(); 1444 values.put(Data.RAW_CONTACT_ID, rawContactId); 1445 values.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); 1446 values.put(Organization.TYPE, Organization.TYPE_WORK); 1447 if (company != null) { 1448 values.put(Organization.COMPANY, company); 1449 } 1450 if (title != null) { 1451 values.put(Organization.TITLE, title); 1452 } 1453 mResolver.insert(Data.CONTENT_URI, values); 1454 } 1455 1456 private void insertEmail(long rawContactId) { 1457 1458 ContentValues values = new ContentValues(); 1459 values.put(Data.RAW_CONTACT_ID, rawContactId); 1460 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1461 values.put(Email.TYPE, Email.TYPE_WORK); 1462 values.put(Email.DATA, "foo (at) acme.com"); 1463 mResolver.insert(Data.CONTENT_URI, values); 1464 1465 int protocol = Im.PROTOCOL_GOOGLE_TALK; 1466 1467 values.clear(); 1468 values.put(StatusUpdates.PROTOCOL, protocol); 1469 values.put(StatusUpdates.IM_HANDLE, email); 1470 values.put(StatusUpdates.IM_ACCOUNT, "foo"); 1471 values.put(StatusUpdates.PRESENCE_STATUS, StatusUpdates.OFFLINE); 1472 values.put(StatusUpdates.CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 1473 values.put(StatusUpdates.PRESENCE_CUSTOM_STATUS, "Coding for Android"); 1474 mResolver.insert(StatusUpdates.CONTENT_URI, values); 1475 } 1476 1477 private void insertPhone(long rawContactId) { 1478 ContentValues values = new ContentValues(); 1479 values.put(Data.RAW_CONTACT_ID, rawContactId); 1480 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1481 values.put(Data.IS_PRIMARY, 1); 1482 values.put(Phone.TYPE, Phone.TYPE_HOME); 1483 values.put(Phone.NUMBER, phone); 1484 mResolver.insert(Data.CONTENT_URI, values); 1485 } 1486 } 1487 } 1488