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