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 902 storeValues(Uri contentUri, long id, Map<String, String> columnValueMap)903 protected void storeValues(Uri contentUri, long id, Map<String, String> columnValueMap) { 904 storeValues(ContentUris.withAppendedId(contentUri, id), columnValueMap); 905 } 906 storeValues(Uri contentUri, Map<String, String> columnValueMap)907 protected void storeValues(Uri contentUri, Map<String, String> columnValueMap) { 908 ContentValues values = new ContentValues(); 909 for (String key : columnValueMap.keySet()) { 910 values.put(key, columnValueMap.get(key)); 911 } 912 mResolver.update(contentUri, values, null, null); 913 } 914 915 storeValue(Uri contentUri, long id, String column, long value)916 protected void storeValue(Uri contentUri, long id, String column, long value) { 917 storeValue(ContentUris.withAppendedId(contentUri, id), column, value); 918 } 919 storeValue(Uri contentUri, String column, long value)920 protected void storeValue(Uri contentUri, String column, long value) { 921 ContentValues values = new ContentValues(); 922 values.put(column, value); 923 924 mResolver.update(contentUri, values, null, null); 925 } 926 assertStoredValue(Uri contentUri, long id, String column, Object expectedValue)927 protected void assertStoredValue(Uri contentUri, long id, String column, Object expectedValue) { 928 assertStoredValue(ContentUris.withAppendedId(contentUri, id), column, expectedValue); 929 } 930 assertStoredValue(Uri rowUri, String column, Object expectedValue)931 protected void assertStoredValue(Uri rowUri, String column, Object expectedValue) { 932 String value = getStoredValue(rowUri, column); 933 if (expectedValue == null) { 934 assertNull("Column value " + column, value); 935 } else { 936 assertEquals("Column value " + column, String.valueOf(expectedValue), value); 937 } 938 } 939 assertStoredValue(Uri rowUri, String selection, String[] selectionArgs, String column, Object expectedValue)940 protected void assertStoredValue(Uri rowUri, String selection, String[] selectionArgs, 941 String column, Object expectedValue) { 942 String value = getStoredValue(rowUri, selection, selectionArgs, column); 943 if (expectedValue == null) { 944 assertNull("Column value " + column, value); 945 } else { 946 assertEquals("Column value " + column, String.valueOf(expectedValue), value); 947 } 948 } 949 getStoredValue(Uri rowUri, String column)950 protected String getStoredValue(Uri rowUri, String column) { 951 return getStoredValue(rowUri, null, null, column); 952 } 953 getStoredValue(Uri uri, String selection, String[] selectionArgs, String column)954 protected String getStoredValue(Uri uri, String selection, String[] selectionArgs, 955 String column) { 956 String value = null; 957 Cursor c = mResolver.query(uri, new String[] { column }, selection, selectionArgs, null); 958 try { 959 assertEquals("Record count for " + uri, 1, c.getCount()); 960 961 if (c.moveToFirst()) { 962 value = c.getString(c.getColumnIndex(column)); 963 } 964 } finally { 965 c.close(); 966 } 967 return value; 968 } 969 getStoredLongValue(Uri uri, String selection, String[] selectionArgs, String column)970 protected Long getStoredLongValue(Uri uri, String selection, String[] selectionArgs, 971 String column) { 972 Long value = null; 973 Cursor c = mResolver.query(uri, new String[] { column }, selection, selectionArgs, null); 974 try { 975 assertEquals("Record count", 1, c.getCount()); 976 977 if (c.moveToFirst()) { 978 value = c.getLong(c.getColumnIndex(column)); 979 } 980 } finally { 981 c.close(); 982 } 983 return value; 984 } 985 getStoredLongValue(Uri uri, String column)986 protected Long getStoredLongValue(Uri uri, String column) { 987 return getStoredLongValue(uri, null, null, column); 988 } 989 assertStoredValues(Uri rowUri, ContentValues expectedValues)990 protected void assertStoredValues(Uri rowUri, ContentValues expectedValues) { 991 assertStoredValues(rowUri, null, null, expectedValues); 992 } 993 assertStoredValues(Uri rowUri, ContentValues... expectedValues)994 protected void assertStoredValues(Uri rowUri, ContentValues... expectedValues) { 995 assertStoredValues(rowUri, null, null, expectedValues); 996 } 997 assertStoredValues(Uri rowUri, String selection, String[] selectionArgs, ContentValues expectedValues)998 protected void assertStoredValues(Uri rowUri, String selection, String[] selectionArgs, 999 ContentValues expectedValues) { 1000 Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null); 1001 try { 1002 assertEquals("Record count", 1, c.getCount()); 1003 c.moveToFirst(); 1004 assertCursorValues(c, expectedValues); 1005 } catch (Error e) { 1006 TestUtils.dumpCursor(c); 1007 throw e; 1008 } finally { 1009 c.close(); 1010 } 1011 } 1012 assertContainsValues(Uri rowUri, ContentValues expectedValues)1013 protected void assertContainsValues(Uri rowUri, ContentValues expectedValues) { 1014 Cursor c = mResolver.query(rowUri, null, null, null, null); 1015 try { 1016 assertEquals("Record count", 1, c.getCount()); 1017 c.moveToFirst(); 1018 assertCursorValuesPartialMatch(c, expectedValues); 1019 } catch (Error e) { 1020 TestUtils.dumpCursor(c); 1021 throw e; 1022 } finally { 1023 c.close(); 1024 } 1025 } 1026 assertStoredValuesWithProjection(Uri rowUri, ContentValues expectedValues)1027 protected void assertStoredValuesWithProjection(Uri rowUri, ContentValues expectedValues) { 1028 assertStoredValuesWithProjection(rowUri, new ContentValues[] {expectedValues}); 1029 } 1030 assertStoredValuesWithProjection(Uri rowUri, ContentValues... expectedValues)1031 protected void assertStoredValuesWithProjection(Uri rowUri, ContentValues... expectedValues) { 1032 assertTrue("Need at least one ContentValues for this test", expectedValues.length > 0); 1033 Cursor c = mResolver.query(rowUri, buildProjection(expectedValues[0]), null, null, null); 1034 try { 1035 assertEquals("Record count", expectedValues.length, c.getCount()); 1036 c.moveToFirst(); 1037 assertCursorValues(c, expectedValues); 1038 } catch (Error e) { 1039 TestUtils.dumpCursor(c); 1040 throw e; 1041 } finally { 1042 c.close(); 1043 } 1044 } 1045 assertStoredValues( Uri rowUri, String selection, String[] selectionArgs, ContentValues... expectedValues)1046 protected void assertStoredValues( 1047 Uri rowUri, String selection, String[] selectionArgs, ContentValues... expectedValues) { 1048 assertStoredValues(mResolver.query(rowUri, null, selection, selectionArgs, null), 1049 expectedValues); 1050 } 1051 assertStoredValues(Cursor c, ContentValues... expectedValues)1052 private void assertStoredValues(Cursor c, ContentValues... expectedValues) { 1053 try { 1054 assertEquals("Record count", expectedValues.length, c.getCount()); 1055 assertCursorValues(c, expectedValues); 1056 } catch (Error e) { 1057 TestUtils.dumpCursor(c); 1058 throw e; 1059 } finally { 1060 c.close(); 1061 } 1062 } 1063 1064 /** 1065 * A variation of {@link #assertStoredValues}, but it queries directly to the DB. 1066 */ assertStoredValuesDb( String sql, String[] selectionArgs, ContentValues... expectedValues)1067 protected void assertStoredValuesDb( 1068 String sql, String[] selectionArgs, ContentValues... expectedValues) { 1069 SQLiteDatabase db = ((ContactsProvider2) getProvider()).getDatabaseHelper() 1070 .getReadableDatabase(); 1071 assertStoredValues(db.rawQuery(sql, selectionArgs), expectedValues); 1072 } 1073 assertStoredValuesOrderly(Uri rowUri, ContentValues... expectedValues)1074 protected void assertStoredValuesOrderly(Uri rowUri, ContentValues... expectedValues) { 1075 assertStoredValuesOrderly(rowUri, null, null, expectedValues); 1076 } 1077 assertStoredValuesOrderly(Uri rowUri, String selection, String[] selectionArgs, ContentValues... expectedValues)1078 protected void assertStoredValuesOrderly(Uri rowUri, String selection, 1079 String[] selectionArgs, ContentValues... expectedValues) { 1080 Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null); 1081 try { 1082 assertEquals("Record count", expectedValues.length, c.getCount()); 1083 assertCursorValuesOrderly(c, expectedValues); 1084 } catch (Error e) { 1085 TestUtils.dumpCursor(c); 1086 throw e; 1087 } finally { 1088 c.close(); 1089 } 1090 } 1091 1092 /** 1093 * Constructs a selection (where clause) out of all supplied values, uses it 1094 * to query the provider and verifies that a single row is returned and it 1095 * has the same values as requested. 1096 */ assertSelection(Uri uri, ContentValues values, String idColumn, long id)1097 protected void assertSelection(Uri uri, ContentValues values, String idColumn, long id) { 1098 assertSelection(uri, values, idColumn, id, null); 1099 } 1100 assertSelectionWithProjection(Uri uri, ContentValues values, String idColumn, long id)1101 public void assertSelectionWithProjection(Uri uri, ContentValues values, String idColumn, 1102 long id) { 1103 assertSelection(uri, values, idColumn, id, buildProjection(values)); 1104 } 1105 assertSelection(Uri uri, ContentValues values, String idColumn, long id, String[] projection)1106 private void assertSelection(Uri uri, ContentValues values, String idColumn, long id, 1107 String[] projection) { 1108 StringBuilder sb = new StringBuilder(); 1109 ArrayList<String> selectionArgs = new ArrayList<String>(values.size()); 1110 if (idColumn != null) { 1111 sb.append(idColumn).append("=").append(id); 1112 } 1113 Set<Map.Entry<String, Object>> entries = values.valueSet(); 1114 for (Map.Entry<String, Object> entry : entries) { 1115 String column = entry.getKey(); 1116 Object value = entry.getValue(); 1117 if (sb.length() != 0) { 1118 sb.append(" AND "); 1119 } 1120 sb.append(column); 1121 if (value == null) { 1122 sb.append(" IS NULL"); 1123 } else { 1124 sb.append("=?"); 1125 selectionArgs.add(String.valueOf(value)); 1126 } 1127 } 1128 1129 Cursor c = mResolver.query(uri, projection, sb.toString(), selectionArgs.toArray(new String[0]), 1130 null); 1131 try { 1132 assertEquals("Record count", 1, c.getCount()); 1133 c.moveToFirst(); 1134 assertCursorValues(c, values); 1135 } catch (Error e) { 1136 TestUtils.dumpCursor(c); 1137 1138 // Dump with no selection. 1139 TestUtils.dumpUri(mResolver, uri); 1140 throw e; 1141 } finally { 1142 c.close(); 1143 } 1144 } 1145 assertCursorValue(Cursor cursor, String column, Object expectedValue)1146 protected void assertCursorValue(Cursor cursor, String column, Object expectedValue) { 1147 String actualValue = cursor.getString(cursor.getColumnIndex(column)); 1148 assertEquals("Column " + column, String.valueOf(expectedValue), 1149 String.valueOf(actualValue)); 1150 } 1151 assertCursorValues(Cursor cursor, ContentValues expectedValues)1152 protected void assertCursorValues(Cursor cursor, ContentValues expectedValues) { 1153 StringBuilder message = new StringBuilder(); 1154 boolean result = equalsWithExpectedValues(cursor, expectedValues, message); 1155 assertTrue(message.toString(), result); 1156 } 1157 assertCursorValuesPartialMatch(Cursor cursor, ContentValues expectedValues)1158 protected void assertCursorValuesPartialMatch(Cursor cursor, ContentValues expectedValues) { 1159 StringBuilder message = new StringBuilder(); 1160 boolean result = expectedValuePartiallyMatches(cursor, expectedValues, message); 1161 assertTrue(message.toString(), result); 1162 } 1163 assertCursorHasAnyRecordMatch(Cursor cursor, ContentValues expectedValues)1164 protected void assertCursorHasAnyRecordMatch(Cursor cursor, ContentValues expectedValues) { 1165 final StringBuilder message = new StringBuilder(); 1166 boolean found = false; 1167 cursor.moveToPosition(-1); 1168 while (cursor.moveToNext()) { 1169 message.setLength(0); 1170 final int pos = cursor.getPosition(); 1171 found = equalsWithExpectedValues(cursor, expectedValues, message); 1172 if (found) { 1173 break; 1174 } 1175 } 1176 assertTrue("Expected values can not be found " + expectedValues + "," + message.toString(), 1177 found); 1178 } 1179 assertCursorValues(Cursor cursor, ContentValues... expectedValues)1180 protected void assertCursorValues(Cursor cursor, ContentValues... expectedValues) { 1181 StringBuilder message = new StringBuilder(); 1182 1183 // In case if expectedValues contains multiple identical values, remember which cursor 1184 // rows are "consumed" to prevent multiple ContentValues from hitting the same row. 1185 final BitSet used = new BitSet(cursor.getCount()); 1186 1187 for (ContentValues v : expectedValues) { 1188 boolean found = false; 1189 cursor.moveToPosition(-1); 1190 while (cursor.moveToNext()) { 1191 final int pos = cursor.getPosition(); 1192 if (used.get(pos)) continue; 1193 found = equalsWithExpectedValues(cursor, v, message); 1194 if (found) { 1195 used.set(pos); 1196 break; 1197 } 1198 } 1199 assertTrue("Expected values can not be found " + v + "," + message.toString(), found); 1200 } 1201 } 1202 assertCursorValuesOrderly(Cursor cursor, ContentValues... expectedValues)1203 public static void assertCursorValuesOrderly(Cursor cursor, ContentValues... expectedValues) { 1204 StringBuilder message = new StringBuilder(); 1205 cursor.moveToPosition(-1); 1206 for (ContentValues v : expectedValues) { 1207 assertTrue(cursor.moveToNext()); 1208 boolean ok = equalsWithExpectedValues(cursor, v, message); 1209 assertTrue("ContentValues didn't match. Pos=" + cursor.getPosition() + ", values=" + 1210 v + message.toString(), ok); 1211 } 1212 } 1213 expectedValuePartiallyMatches(Cursor cursor, ContentValues expectedValues, StringBuilder msgBuffer)1214 private boolean expectedValuePartiallyMatches(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 String expectedValue = expectedValues.getAsString(column); 1223 String value = cursor.getString(cursor.getColumnIndex(column)); 1224 if (value != null && !value.contains(expectedValue)) { 1225 msgBuffer.append(" Column value ").append(column).append(" expected to contain <") 1226 .append(expectedValue).append(">, but was <").append(value).append('>'); 1227 return false; 1228 } 1229 } 1230 return true; 1231 } 1232 equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues, StringBuilder msgBuffer)1233 private static boolean equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues, 1234 StringBuilder msgBuffer) { 1235 for (String column : expectedValues.keySet()) { 1236 int index = cursor.getColumnIndex(column); 1237 if (index == -1) { 1238 msgBuffer.append(" No such column: ").append(column); 1239 return false; 1240 } 1241 Object expectedValue = expectedValues.get(column); 1242 String value; 1243 if (expectedValue instanceof byte[]) { 1244 expectedValue = Hex.encodeHex((byte[])expectedValue, false); 1245 value = Hex.encodeHex(cursor.getBlob(index), false); 1246 } else { 1247 expectedValue = expectedValues.getAsString(column); 1248 value = cursor.getString(cursor.getColumnIndex(column)); 1249 } 1250 if (expectedValue != null && !expectedValue.equals(value) || value != null 1251 && !value.equals(expectedValue)) { 1252 msgBuffer 1253 .append(" Column value ") 1254 .append(column) 1255 .append(" expected <") 1256 .append(expectedValue) 1257 .append(">, but was <") 1258 .append(value) 1259 .append('>'); 1260 return false; 1261 } 1262 } 1263 return true; 1264 } 1265 1266 private static final String[] DATA_USAGE_PROJECTION = 1267 new String[] {Data.DATA1, Data.TIMES_USED, Data.LAST_TIME_USED}; 1268 assertDataUsageZero(Uri uri, String data1)1269 protected void assertDataUsageZero(Uri uri, String data1) { 1270 final Cursor cursor = mResolver.query(uri, DATA_USAGE_PROJECTION, null, null, 1271 null); 1272 try { 1273 dumpCursor(cursor); 1274 assertCursorHasAnyRecordMatch(cursor, cv(Data.DATA1, data1, Data.TIMES_USED, 0, 1275 Data.LAST_TIME_USED, 0)); 1276 } finally { 1277 cursor.close(); 1278 } 1279 } 1280 buildProjection(ContentValues values)1281 private String[] buildProjection(ContentValues values) { 1282 String[] projection = new String[values.size()]; 1283 Iterator<Entry<String, Object>> iter = values.valueSet().iterator(); 1284 for (int i = 0; i < projection.length; i++) { 1285 projection[i] = iter.next().getKey(); 1286 } 1287 return projection; 1288 } 1289 getCount(Uri uri)1290 protected int getCount(Uri uri) { 1291 return getCount(uri, null, null); 1292 } 1293 getCount(Uri uri, String selection, String[] selectionArgs)1294 protected int getCount(Uri uri, String selection, String[] selectionArgs) { 1295 Cursor c = mResolver.query(uri, null, selection, selectionArgs, null); 1296 try { 1297 return c.getCount(); 1298 } finally { 1299 c.close(); 1300 } 1301 } 1302 dump(ContentResolver resolver, boolean aggregatedOnly)1303 public static void dump(ContentResolver resolver, boolean aggregatedOnly) { 1304 String[] projection = new String[] { 1305 Contacts._ID, 1306 Contacts.DISPLAY_NAME 1307 }; 1308 String selection = null; 1309 if (aggregatedOnly) { 1310 selection = Contacts._ID 1311 + " IN (SELECT contact_id" + 1312 " FROM raw_contacts GROUP BY contact_id HAVING count(*) > 1)"; 1313 } 1314 1315 Cursor c = resolver.query(Contacts.CONTENT_URI, projection, selection, null, 1316 Contacts.DISPLAY_NAME); 1317 while(c.moveToNext()) { 1318 long contactId = c.getLong(0); 1319 Log.i("Contact ", String.format("%5d %s", contactId, c.getString(1))); 1320 dumpRawContacts(resolver, contactId); 1321 Log.i(" ", "."); 1322 } 1323 c.close(); 1324 } 1325 dumpRawContacts(ContentResolver resolver, long contactId)1326 private static void dumpRawContacts(ContentResolver resolver, long contactId) { 1327 String[] projection = new String[] { 1328 RawContacts._ID, 1329 }; 1330 Cursor c = resolver.query(RawContacts.CONTENT_URI, projection, RawContacts.CONTACT_ID + "=" 1331 + contactId, null, null); 1332 while(c.moveToNext()) { 1333 long rawContactId = c.getLong(0); 1334 Log.i("RawContact", String.format(" %-5d", rawContactId)); 1335 dumpData(resolver, rawContactId); 1336 } 1337 c.close(); 1338 } 1339 dumpData(ContentResolver resolver, long rawContactId)1340 private static void dumpData(ContentResolver resolver, long rawContactId) { 1341 String[] projection = new String[] { 1342 Data.MIMETYPE, 1343 Data.DATA1, 1344 Data.DATA2, 1345 Data.DATA3, 1346 }; 1347 Cursor c = resolver.query(Data.CONTENT_URI, projection, Data.RAW_CONTACT_ID + "=" 1348 + rawContactId, null, Data.MIMETYPE); 1349 while(c.moveToNext()) { 1350 String mimetype = c.getString(0); 1351 if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) { 1352 Log.i("Photo ", ""); 1353 } else { 1354 mimetype = mimetype.substring(mimetype.indexOf('/') + 1); 1355 Log.i("Data ", String.format(" %-10s %s,%s,%s", mimetype, 1356 c.getString(1), c.getString(2), c.getString(3))); 1357 } 1358 } 1359 c.close(); 1360 } 1361 assertNetworkNotified(boolean expected)1362 protected void assertNetworkNotified(boolean expected) { 1363 assertEquals(expected, (getContactsProvider()).isNetworkNotified()); 1364 } 1365 assertProjection(Uri uri, String[] expectedProjection)1366 protected void assertProjection(Uri uri, String[] expectedProjection) { 1367 Cursor cursor = mResolver.query(uri, null, "0", null, null); 1368 String[] actualProjection = cursor.getColumnNames(); 1369 MoreAsserts.assertEquals("Incorrect projection for URI: " + uri, 1370 Sets.newHashSet(expectedProjection), Sets.newHashSet(actualProjection)); 1371 cursor.close(); 1372 } 1373 assertContainProjection(Uri uri, String[] mustHaveProjection)1374 protected void assertContainProjection(Uri uri, String[] mustHaveProjection) { 1375 Cursor cursor = mResolver.query(uri, null, "0", null, null); 1376 String[] actualProjection = cursor.getColumnNames(); 1377 Set<String> actualProjectionSet = Sets.newHashSet(actualProjection); 1378 Set<String> mustHaveProjectionSet = Sets.newHashSet(mustHaveProjection); 1379 actualProjectionSet.retainAll(mustHaveProjectionSet); 1380 MoreAsserts.assertEquals(mustHaveProjectionSet, actualProjectionSet); 1381 cursor.close(); 1382 } 1383 assertRowCount(int expectedCount, Uri uri, String selection, String[] args)1384 protected void assertRowCount(int expectedCount, Uri uri, String selection, String[] args) { 1385 Cursor cursor = mResolver.query(uri, null, selection, args, null); 1386 1387 try { 1388 assertEquals(expectedCount, cursor.getCount()); 1389 } catch (Error e) { 1390 TestUtils.dumpCursor(cursor); 1391 throw e; 1392 } finally { 1393 cursor.close(); 1394 } 1395 } 1396 assertLastModified(Uri uri, long time)1397 protected void assertLastModified(Uri uri, long time) { 1398 Cursor c = mResolver.query(uri, null, null, null, null); 1399 c.moveToFirst(); 1400 int index = c.getColumnIndex(CallLog.Calls.LAST_MODIFIED); 1401 long timeStamp = c.getLong(index); 1402 assertEquals(timeStamp, time); 1403 } 1404 1405 /** 1406 * Asserts the equality of two Uri objects, ignoring the order of the query parameters. 1407 */ assertUriEquals(Uri expected, Uri actual)1408 protected static void assertUriEquals(Uri expected, Uri actual) { 1409 assertEquals(expected.getScheme(), actual.getScheme()); 1410 assertEquals(expected.getAuthority(), actual.getAuthority()); 1411 assertEquals(expected.getPath(), actual.getPath()); 1412 assertEquals(expected.getFragment(), actual.getFragment()); 1413 Set<String> expectedParameterNames = expected.getQueryParameterNames(); 1414 Set<String> actualParameterNames = actual.getQueryParameterNames(); 1415 assertEquals(expectedParameterNames.size(), actualParameterNames.size()); 1416 assertTrue(expectedParameterNames.containsAll(actualParameterNames)); 1417 for (String parameterName : expectedParameterNames) { 1418 assertEquals(expected.getQueryParameter(parameterName), 1419 actual.getQueryParameter(parameterName)); 1420 } 1421 1422 } 1423 setTimeForTest(Long time)1424 protected void setTimeForTest(Long time) { 1425 Uri uri = Calls.CONTENT_URI.buildUpon() 1426 .appendQueryParameter(CallLogProvider.PARAM_KEY_QUERY_FOR_TESTING, "1") 1427 .appendQueryParameter(CallLogProvider.PARAM_KEY_SET_TIME_FOR_TESTING, 1428 time == null ? "null" : time.toString()) 1429 .build(); 1430 mResolver.query(uri, null, null, null, null); 1431 } 1432 insertRawContact(ContentValues values)1433 protected Uri insertRawContact(ContentValues values) { 1434 return TestUtils.insertRawContact(mResolver, 1435 getContactsProvider().getDatabaseHelper(), values); 1436 } 1437 insertProfileRawContact(ContentValues values)1438 protected Uri insertProfileRawContact(ContentValues values) { 1439 return TestUtils.insertProfileRawContact(mResolver, 1440 getContactsProvider().getProfileProviderForTest().getDatabaseHelper(), values); 1441 } 1442 1443 protected class VCardTestUriCreator { 1444 private String mLookup1; 1445 private String mLookup2; 1446 VCardTestUriCreator(String lookup1, String lookup2)1447 public VCardTestUriCreator(String lookup1, String lookup2) { 1448 super(); 1449 mLookup1 = lookup1; 1450 mLookup2 = lookup2; 1451 } 1452 getUri1()1453 public Uri getUri1() { 1454 return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1); 1455 } 1456 getUri2()1457 public Uri getUri2() { 1458 return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2); 1459 } 1460 getCombinedUri()1461 public Uri getCombinedUri() { 1462 return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, 1463 Uri.encode(mLookup1 + ":" + mLookup2)); 1464 } 1465 } 1466 createVCardTestContacts()1467 protected VCardTestUriCreator createVCardTestContacts() { 1468 final long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount, 1469 RawContacts.SOURCE_ID, "4:12"); 1470 DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Doe"); 1471 1472 final long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccount, 1473 RawContacts.SOURCE_ID, "3:4%121"); 1474 DataUtil.insertStructuredName(mResolver, rawContactId2, "Jane", "Doh"); 1475 1476 final long contactId1 = queryContactId(rawContactId1); 1477 final long contactId2 = queryContactId(rawContactId2); 1478 final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1); 1479 final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2); 1480 final String lookup1 = 1481 Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2)); 1482 final String lookup2 = 1483 Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2)); 1484 return new VCardTestUriCreator(lookup1, lookup2); 1485 } 1486 readToEnd(FileInputStream inputStream)1487 protected String readToEnd(FileInputStream inputStream) { 1488 try { 1489 System.out.println("DECLARED INPUT STREAM LENGTH: " + inputStream.available()); 1490 int ch; 1491 StringBuilder stringBuilder = new StringBuilder(); 1492 int index = 0; 1493 while (true) { 1494 ch = inputStream.read(); 1495 System.out.println("READ CHARACTER: " + index + " " + ch); 1496 if (ch == -1) { 1497 break; 1498 } 1499 stringBuilder.append((char)ch); 1500 index++; 1501 } 1502 return stringBuilder.toString(); 1503 } catch (IOException e) { 1504 return null; 1505 } 1506 } 1507 1508 /** 1509 * A contact in the database, and the attributes used to create it. Construct using 1510 * {@link GoldenContactBuilder#build()}. 1511 */ 1512 public final class GoldenContact { 1513 1514 private final long rawContactId; 1515 1516 private final long contactId; 1517 1518 private final String givenName; 1519 1520 private final String familyName; 1521 1522 private final String nickname; 1523 1524 private final byte[] photo; 1525 1526 private final String company; 1527 1528 private final String title; 1529 1530 private final String phone; 1531 1532 private final String email; 1533 GoldenContact(GoldenContactBuilder builder, long rawContactId, long contactId)1534 private GoldenContact(GoldenContactBuilder builder, long rawContactId, long contactId) { 1535 1536 this.rawContactId = rawContactId; 1537 this.contactId = contactId; 1538 givenName = builder.givenName; 1539 familyName = builder.familyName; 1540 nickname = builder.nickname; 1541 photo = builder.photo; 1542 company = builder.company; 1543 title = builder.title; 1544 phone = builder.phone; 1545 email = builder.email; 1546 } 1547 delete()1548 public void delete() { 1549 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 1550 mResolver.delete(rawContactUri, null, null); 1551 } 1552 1553 /** 1554 * Returns the index of the contact in table "raw_contacts" 1555 */ getRawContactId()1556 public long getRawContactId() { 1557 return rawContactId; 1558 } 1559 1560 /** 1561 * Returns the index of the contact in table "contacts" 1562 */ getContactId()1563 public long getContactId() { 1564 return contactId; 1565 } 1566 1567 /** 1568 * Returns the lookup key for the contact. 1569 */ getLookupKey()1570 public String getLookupKey() { 1571 return queryLookupKey(contactId); 1572 } 1573 1574 /** 1575 * Returns the contact's given name. 1576 */ getGivenName()1577 public String getGivenName() { 1578 return givenName; 1579 } 1580 1581 /** 1582 * Returns the contact's family name. 1583 */ getFamilyName()1584 public String getFamilyName() { 1585 return familyName; 1586 } 1587 1588 /** 1589 * Returns the contact's nickname. 1590 */ getNickname()1591 public String getNickname() { 1592 return nickname; 1593 } 1594 1595 /** 1596 * Return's the contact's photo 1597 */ getPhoto()1598 public byte[] getPhoto() { 1599 return photo; 1600 } 1601 1602 /** 1603 * Return's the company at which the contact works. 1604 */ getCompany()1605 public String getCompany() { 1606 return company; 1607 } 1608 1609 /** 1610 * Returns the contact's job title. 1611 */ getTitle()1612 public String getTitle() { 1613 return title; 1614 } 1615 1616 /** 1617 * Returns the contact's phone number 1618 */ getPhone()1619 public String getPhone() { 1620 return phone; 1621 } 1622 1623 /** 1624 * Returns the contact's email address 1625 */ getEmail()1626 public String getEmail() { 1627 return email; 1628 } 1629 } 1630 1631 /** 1632 * Builds {@link GoldenContact} objects. Unspecified boolean objects default to false. 1633 * Unspecified String objects default to null. 1634 */ 1635 public final class GoldenContactBuilder { 1636 1637 private String givenName; 1638 1639 private String familyName; 1640 1641 private String nickname; 1642 1643 private byte[] photo; 1644 1645 private String company; 1646 1647 private String title; 1648 1649 private String phone; 1650 1651 private String email; 1652 1653 /** 1654 * The contact's given and family names. 1655 * 1656 * TODO(dplotnikov): inline, or should we require them to set both names if they set either? 1657 */ name(String givenName, String familyName)1658 public GoldenContactBuilder name(String givenName, String familyName) { 1659 return givenName(givenName).familyName(familyName); 1660 } 1661 1662 /** 1663 * The contact's given name. 1664 */ givenName(String value)1665 public GoldenContactBuilder givenName(String value) { 1666 givenName = value; 1667 return this; 1668 } 1669 1670 /** 1671 * The contact's family name. 1672 */ familyName(String value)1673 public GoldenContactBuilder familyName(String value) { 1674 familyName = value; 1675 return this; 1676 } 1677 1678 /** 1679 * The contact's nickname. 1680 */ nickname(String value)1681 public GoldenContactBuilder nickname(String value) { 1682 nickname = value; 1683 return this; 1684 } 1685 1686 /** 1687 * The contact's photo. 1688 */ photo(byte[] value)1689 public GoldenContactBuilder photo(byte[] value) { 1690 photo = value; 1691 return this; 1692 } 1693 1694 /** 1695 * The company at which the contact works. 1696 */ company(String value)1697 public GoldenContactBuilder company(String value) { 1698 company = value; 1699 return this; 1700 } 1701 1702 /** 1703 * The contact's job title. 1704 */ title(String value)1705 public GoldenContactBuilder title(String value) { 1706 title = value; 1707 return this; 1708 } 1709 1710 /** 1711 * The contact's phone number. 1712 */ phone(String value)1713 public GoldenContactBuilder phone(String value) { 1714 phone = value; 1715 return this; 1716 } 1717 1718 /** 1719 * The contact's email address; also sets their IM status to {@link StatusUpdates#OFFLINE} 1720 * with a presence of "Coding for Android". 1721 */ email(String value)1722 public GoldenContactBuilder email(String value) { 1723 email = value; 1724 return this; 1725 } 1726 1727 /** 1728 * Builds the {@link GoldenContact} specified by this builder. 1729 */ build()1730 public GoldenContact build() { 1731 1732 final long groupId = createGroup(mAccount, "gsid1", "title1"); 1733 1734 long rawContactId = RawContactUtil.createRawContact(mResolver); 1735 insertGroupMembership(rawContactId, groupId); 1736 1737 if (givenName != null || familyName != null) { 1738 DataUtil.insertStructuredName(mResolver, rawContactId, givenName, familyName); 1739 } 1740 if (nickname != null) { 1741 insertNickname(rawContactId, nickname); 1742 } 1743 if (photo != null) { 1744 insertPhoto(rawContactId); 1745 } 1746 if (company != null || title != null) { 1747 insertOrganization(rawContactId); 1748 } 1749 if (email != null) { 1750 insertEmail(rawContactId); 1751 } 1752 if (phone != null) { 1753 insertPhone(rawContactId); 1754 } 1755 1756 long contactId = queryContactId(rawContactId); 1757 1758 return new GoldenContact(this, rawContactId, contactId); 1759 } 1760 insertPhoto(long rawContactId)1761 private void insertPhoto(long rawContactId) { 1762 ContentValues values = new ContentValues(); 1763 values.put(Data.RAW_CONTACT_ID, rawContactId); 1764 values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 1765 values.put(Photo.PHOTO, photo); 1766 mResolver.insert(Data.CONTENT_URI, values); 1767 } 1768 insertOrganization(long rawContactId)1769 private void insertOrganization(long rawContactId) { 1770 1771 ContentValues values = new ContentValues(); 1772 values.put(Data.RAW_CONTACT_ID, rawContactId); 1773 values.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); 1774 values.put(Organization.TYPE, Organization.TYPE_WORK); 1775 if (company != null) { 1776 values.put(Organization.COMPANY, company); 1777 } 1778 if (title != null) { 1779 values.put(Organization.TITLE, title); 1780 } 1781 mResolver.insert(Data.CONTENT_URI, values); 1782 } 1783 insertEmail(long rawContactId)1784 private void insertEmail(long rawContactId) { 1785 1786 ContentValues values = new ContentValues(); 1787 values.put(Data.RAW_CONTACT_ID, rawContactId); 1788 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 1789 values.put(Email.TYPE, Email.TYPE_WORK); 1790 values.put(Email.DATA, "[email protected]"); 1791 mResolver.insert(Data.CONTENT_URI, values); 1792 1793 int protocol = Im.PROTOCOL_GOOGLE_TALK; 1794 1795 values.clear(); 1796 values.put(StatusUpdates.PROTOCOL, protocol); 1797 values.put(StatusUpdates.IM_HANDLE, email); 1798 values.put(StatusUpdates.IM_ACCOUNT, "foo"); 1799 values.put(StatusUpdates.PRESENCE_STATUS, StatusUpdates.OFFLINE); 1800 values.put(StatusUpdates.CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 1801 values.put(StatusUpdates.PRESENCE_CUSTOM_STATUS, "Coding for Android"); 1802 mResolver.insert(StatusUpdates.CONTENT_URI, values); 1803 } 1804 insertPhone(long rawContactId)1805 private void insertPhone(long rawContactId) { 1806 ContentValues values = new ContentValues(); 1807 values.put(Data.RAW_CONTACT_ID, rawContactId); 1808 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1809 values.put(Data.IS_PRIMARY, 1); 1810 values.put(Phone.TYPE, Phone.TYPE_HOME); 1811 values.put(Phone.NUMBER, phone); 1812 mResolver.insert(Data.CONTENT_URI, values); 1813 } 1814 } 1815 } 1816