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