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