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