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