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