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