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