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