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