1 /* 2 * Copyright (C) 2010 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 android.provider.cts.contacts; 18 19 20 import static android.provider.ContactsContract.CommonDataKinds; 21 22 import android.content.ContentProviderClient; 23 import android.content.ContentResolver; 24 import android.content.ContentValues; 25 import android.net.Uri; 26 import android.os.SystemClock; 27 import android.provider.ContactsContract; 28 import android.provider.ContactsContract.CommonDataKinds.Callable; 29 import android.provider.ContactsContract.CommonDataKinds.Contactables; 30 import android.provider.ContactsContract.CommonDataKinds.Email; 31 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 32 import android.provider.ContactsContract.CommonDataKinds.Organization; 33 import android.provider.ContactsContract.CommonDataKinds.Phone; 34 import android.provider.ContactsContract.CommonDataKinds.SipAddress; 35 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 36 import android.provider.ContactsContract.Contacts; 37 import android.provider.ContactsContract.Contacts.Entity; 38 import android.provider.ContactsContract.Data; 39 import android.provider.ContactsContract.Directory; 40 import android.provider.ContactsContract.RawContacts; 41 import android.provider.ContactsContract.RawContactsEntity; 42 import android.provider.cts.contacts.ContactsContract_TestDataBuilder.TestContact; 43 import android.provider.cts.contacts.ContactsContract_TestDataBuilder.TestData; 44 import android.provider.cts.contacts.ContactsContract_TestDataBuilder.TestRawContact; 45 import android.test.InstrumentationTestCase; 46 47 import java.util.ArrayList; 48 49 public class ContactsContract_DataTest extends InstrumentationTestCase { 50 private ContentResolver mResolver; 51 private ContactsContract_TestDataBuilder mBuilder; 52 53 static final String[] DATA_PROJECTION = new String[]{ 54 Data._ID, 55 Data.RAW_CONTACT_ID, 56 Data.CONTACT_ID, 57 Data.NAME_RAW_CONTACT_ID, 58 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 59 Data.DATA1, 60 Data.DATA2, 61 Data.DATA3, 62 Data.DATA4, 63 Data.DATA5, 64 Data.DATA6, 65 Data.DATA7, 66 Data.DATA8, 67 Data.DATA9, 68 Data.DATA10, 69 Data.DATA11, 70 Data.DATA12, 71 Data.DATA13, 72 Data.DATA14, 73 Data.DATA15, 74 Data.CARRIER_PRESENCE, 75 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 76 Data.PREFERRED_PHONE_ACCOUNT_ID, 77 Data.DATA_VERSION, 78 Data.IS_PRIMARY, 79 Data.IS_SUPER_PRIMARY, 80 Data.MIMETYPE, 81 Data.RES_PACKAGE, 82 Data.SYNC1, 83 Data.SYNC2, 84 Data.SYNC3, 85 Data.SYNC4, 86 GroupMembership.GROUP_SOURCE_ID, 87 Data.PRESENCE, 88 Data.CHAT_CAPABILITY, 89 Data.STATUS, 90 Data.STATUS_TIMESTAMP, 91 Data.STATUS_RES_PACKAGE, 92 Data.STATUS_LABEL, 93 Data.STATUS_ICON, 94 RawContacts.ACCOUNT_NAME, 95 RawContacts.ACCOUNT_TYPE, 96 RawContacts.DATA_SET, 97 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 98 RawContacts.DIRTY, 99 RawContacts.SOURCE_ID, 100 RawContacts.VERSION, 101 Contacts.CUSTOM_RINGTONE, 102 Contacts.DISPLAY_NAME, 103 Contacts.DISPLAY_NAME_ALTERNATIVE, 104 Contacts.DISPLAY_NAME_SOURCE, 105 Contacts.IN_DEFAULT_DIRECTORY, 106 Contacts.IN_VISIBLE_GROUP, 107 Contacts.LAST_TIME_CONTACTED, 108 Contacts.LOOKUP_KEY, 109 Contacts.PHONETIC_NAME, 110 Contacts.PHONETIC_NAME_STYLE, 111 Contacts.PHOTO_ID, 112 Contacts.PHOTO_FILE_ID, 113 Contacts.PHOTO_URI, 114 Contacts.PHOTO_THUMBNAIL_URI, 115 Contacts.SEND_TO_VOICEMAIL, 116 Contacts.SORT_KEY_ALTERNATIVE, 117 Contacts.SORT_KEY_PRIMARY, 118 Contacts.STARRED, 119 Contacts.PINNED, 120 Contacts.TIMES_CONTACTED, 121 Contacts.HAS_PHONE_NUMBER, 122 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 123 Contacts.CONTACT_PRESENCE, 124 Contacts.CONTACT_CHAT_CAPABILITY, 125 Contacts.CONTACT_STATUS, 126 Contacts.CONTACT_STATUS_TIMESTAMP, 127 Contacts.CONTACT_STATUS_RES_PACKAGE, 128 Contacts.CONTACT_STATUS_LABEL, 129 Contacts.CONTACT_STATUS_ICON, 130 Data.TIMES_USED, 131 Data.LAST_TIME_USED}; 132 133 static final String[] RAW_CONTACTS_ENTITY_PROJECTION = new String[]{ 134 }; 135 136 static final String[] NTITY_PROJECTION = new String[]{ 137 }; 138 139 private static ContentValues[] sContentValues = new ContentValues[7]; 140 static { 141 ContentValues cv1 = new ContentValues(); cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale")142 cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale"); cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)143 cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); cv1.put(Email.DATA, "tamale@acme.com")144 cv1.put(Email.DATA, "tamale@acme.com"); cv1.put(Email.TYPE, Email.TYPE_HOME)145 cv1.put(Email.TYPE, Email.TYPE_HOME); 146 sContentValues[0] = cv1; 147 148 ContentValues cv2 = new ContentValues(); cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale")149 cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale"); cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)150 cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); cv2.put(Phone.DATA, "510-123-5769")151 cv2.put(Phone.DATA, "510-123-5769"); cv2.put(Phone.TYPE, Phone.TYPE_HOME)152 cv2.put(Phone.TYPE, Phone.TYPE_HOME); 153 sContentValues[1] = cv2; 154 155 ContentValues cv3 = new ContentValues(); cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale")156 cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale"); cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)157 cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); cv3.put(Email.DATA, "hot@google.com")158 cv3.put(Email.DATA, "hot@google.com"); cv3.put(Email.TYPE, Email.TYPE_WORK)159 cv3.put(Email.TYPE, Email.TYPE_WORK); 160 sContentValues[2] = cv3; 161 162 ContentValues cv4 = new ContentValues(); cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago")163 cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago"); cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)164 cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); cv4.put(Email.DATA, "eggs@farmers.org")165 cv4.put(Email.DATA, "eggs@farmers.org"); cv4.put(Email.TYPE, Email.TYPE_HOME)166 cv4.put(Email.TYPE, Email.TYPE_HOME); 167 sContentValues[3] = cv4; 168 169 ContentValues cv5 = new ContentValues(); cv5.put(Contacts.DISPLAY_NAME, "John Doe")170 cv5.put(Contacts.DISPLAY_NAME, "John Doe"); cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)171 cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); cv5.put(Email.DATA, "doeassociates@deer.com")172 cv5.put(Email.DATA, "doeassociates@deer.com"); cv5.put(Email.TYPE, Email.TYPE_WORK)173 cv5.put(Email.TYPE, Email.TYPE_WORK); 174 sContentValues[4] = cv5; 175 176 ContentValues cv6 = new ContentValues(); cv6.put(Contacts.DISPLAY_NAME, "John Doe")177 cv6.put(Contacts.DISPLAY_NAME, "John Doe"); cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)178 cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); cv6.put(Phone.DATA, "518-354-1111")179 cv6.put(Phone.DATA, "518-354-1111"); cv6.put(Phone.TYPE, Phone.TYPE_HOME)180 cv6.put(Phone.TYPE, Phone.TYPE_HOME); 181 sContentValues[5] = cv6; 182 183 ContentValues cv7 = new ContentValues(); cv7.put(Contacts.DISPLAY_NAME, "Cold Tamago")184 cv7.put(Contacts.DISPLAY_NAME, "Cold Tamago"); cv7.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE)185 cv7.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE); cv7.put(SipAddress.DATA, "mysip@sipaddress.com")186 cv7.put(SipAddress.DATA, "mysip@sipaddress.com"); cv7.put(SipAddress.TYPE, SipAddress.TYPE_HOME)187 cv7.put(SipAddress.TYPE, SipAddress.TYPE_HOME); 188 sContentValues[6] = cv7; 189 } 190 191 private TestRawContact[] mRawContacts = new TestRawContact[3]; 192 193 @Override setUp()194 protected void setUp() throws Exception { 195 super.setUp(); 196 mResolver = getInstrumentation().getTargetContext().getContentResolver(); 197 ContentProviderClient provider = 198 mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY); 199 mBuilder = new ContactsContract_TestDataBuilder(provider); 200 } 201 202 @Override tearDown()203 protected void tearDown() throws Exception { 204 super.tearDown(); 205 mBuilder.cleanup(); 206 } 207 testGetLookupUriBySourceId()208 public void testGetLookupUriBySourceId() throws Exception { 209 TestRawContact rawContact = mBuilder.newRawContact() 210 .with(RawContacts.ACCOUNT_TYPE, "test_type") 211 .with(RawContacts.ACCOUNT_NAME, "test_name") 212 .with(RawContacts.SOURCE_ID, "source_id") 213 .insert(); 214 215 // TODO remove this. The method under test is currently broken: it will not 216 // work without at least one data row in the raw contact. 217 TestData data = rawContact.newDataRow(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) 218 .with(CommonDataKinds.StructuredName.DISPLAY_NAME, "test name") 219 .insert(); 220 221 Uri lookupUri = Data.getContactLookupUri(mResolver, data.getUri()); 222 assertNotNull("Could not produce a lookup URI", lookupUri); 223 224 TestContact lookupContact = mBuilder.newContact().setUri(lookupUri).load(); 225 assertEquals("Lookup URI matched the wrong contact", 226 lookupContact.getId(), data.load().getRawContact().load().getContactId()); 227 } 228 testDataProjection()229 public void testDataProjection() throws Exception { 230 TestRawContact rawContact = mBuilder.newRawContact() 231 .with(RawContacts.ACCOUNT_TYPE, "test_type") 232 .with(RawContacts.ACCOUNT_NAME, "test_name") 233 .with(RawContacts.SOURCE_ID, "source_id") 234 .insert(); 235 TestData data = rawContact.newDataRow(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) 236 .with(CommonDataKinds.StructuredName.DISPLAY_NAME, "test name") 237 .insert(); 238 239 DatabaseAsserts.checkProjection(mResolver, Data.CONTENT_URI, 240 DATA_PROJECTION, 241 new long[]{data.load().getId()} 242 ); 243 } 244 testRawContactsEntityProjection()245 public void testRawContactsEntityProjection() throws Exception { 246 TestRawContact rawContact = mBuilder.newRawContact() 247 .with(RawContacts.ACCOUNT_TYPE, "test_type") 248 .with(RawContacts.ACCOUNT_NAME, "test_name") 249 .with(RawContacts.SOURCE_ID, "source_id") 250 .insert(); 251 TestData data = rawContact.newDataRow(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) 252 .with(CommonDataKinds.StructuredName.DISPLAY_NAME, "test name") 253 .insert(); 254 255 DatabaseAsserts.checkProjection(mResolver, RawContactsEntity.CONTENT_URI, 256 new String[]{ 257 RawContacts._ID, 258 RawContacts.CONTACT_ID, 259 RawContacts.Entity.DATA_ID, 260 RawContacts.DELETED, 261 RawContacts.STARRED, 262 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 263 RawContacts.ACCOUNT_NAME, 264 RawContacts.ACCOUNT_TYPE, 265 RawContacts.DATA_SET, 266 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 267 RawContacts.DIRTY, 268 RawContacts.SOURCE_ID, 269 RawContacts.BACKUP_ID, 270 RawContacts.VERSION, 271 RawContacts.SYNC1, 272 RawContacts.SYNC2, 273 RawContacts.SYNC3, 274 RawContacts.SYNC4, 275 Data.DATA1, 276 Data.DATA2, 277 Data.DATA3, 278 Data.DATA4, 279 Data.DATA5, 280 Data.DATA6, 281 Data.DATA7, 282 Data.DATA8, 283 Data.DATA9, 284 Data.DATA10, 285 Data.DATA11, 286 Data.DATA12, 287 Data.DATA13, 288 Data.DATA14, 289 Data.DATA15, 290 Data.CARRIER_PRESENCE, 291 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 292 Data.PREFERRED_PHONE_ACCOUNT_ID, 293 Data.DATA_VERSION, 294 Data.IS_PRIMARY, 295 Data.IS_SUPER_PRIMARY, 296 Data.MIMETYPE, 297 Data.RES_PACKAGE, 298 Data.SYNC1, 299 Data.SYNC2, 300 Data.SYNC3, 301 Data.SYNC4, 302 GroupMembership.GROUP_SOURCE_ID}, 303 new long[]{rawContact.getId()} 304 ); 305 } 306 testEntityProjection()307 public void testEntityProjection() throws Exception { 308 TestRawContact rawContact = mBuilder.newRawContact() 309 .with(RawContacts.ACCOUNT_TYPE, "test_type") 310 .with(RawContacts.ACCOUNT_NAME, "test_name") 311 .with(RawContacts.SOURCE_ID, "source_id") 312 .insert(); 313 TestData data = rawContact.newDataRow(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) 314 .with(CommonDataKinds.StructuredName.DISPLAY_NAME, "test name") 315 .insert(); 316 long contactId = rawContact.load().getContactId(); 317 318 DatabaseAsserts.checkProjection(mResolver, Contacts.CONTENT_URI.buildUpon().appendPath( 319 String.valueOf(contactId)).appendPath( 320 Entity.CONTENT_DIRECTORY).build(), 321 new String[]{ 322 Contacts.Entity._ID, 323 Contacts.Entity.CONTACT_ID, 324 Contacts.Entity.RAW_CONTACT_ID, 325 Contacts.Entity.DATA_ID, 326 Contacts.Entity.NAME_RAW_CONTACT_ID, 327 Contacts.Entity.DELETED, 328 Contacts.IS_USER_PROFILE, 329 Contacts.CUSTOM_RINGTONE, 330 Contacts.DISPLAY_NAME, 331 Contacts.DISPLAY_NAME_ALTERNATIVE, 332 Contacts.DISPLAY_NAME_SOURCE, 333 Contacts.IN_DEFAULT_DIRECTORY, 334 Contacts.IN_VISIBLE_GROUP, 335 Contacts.LAST_TIME_CONTACTED, 336 Contacts.LOOKUP_KEY, 337 Contacts.PHONETIC_NAME, 338 Contacts.PHONETIC_NAME_STYLE, 339 Contacts.PHOTO_ID, 340 Contacts.PHOTO_FILE_ID, 341 Contacts.PHOTO_URI, 342 Contacts.PHOTO_THUMBNAIL_URI, 343 Contacts.SEND_TO_VOICEMAIL, 344 Contacts.SORT_KEY_ALTERNATIVE, 345 Contacts.SORT_KEY_PRIMARY, 346 Contacts.STARRED, 347 Contacts.PINNED, 348 Contacts.TIMES_CONTACTED, 349 Contacts.HAS_PHONE_NUMBER, 350 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 351 Contacts.CONTACT_PRESENCE, 352 Contacts.CONTACT_CHAT_CAPABILITY, 353 Contacts.CONTACT_STATUS, 354 Contacts.CONTACT_STATUS_TIMESTAMP, 355 Contacts.CONTACT_STATUS_RES_PACKAGE, 356 Contacts.CONTACT_STATUS_LABEL, 357 Contacts.CONTACT_STATUS_ICON, 358 RawContacts.ACCOUNT_NAME, 359 RawContacts.ACCOUNT_TYPE, 360 RawContacts.DATA_SET, 361 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 362 RawContacts.DIRTY, 363 RawContacts.SOURCE_ID, 364 RawContacts.BACKUP_ID, 365 RawContacts.VERSION, 366 RawContacts.SYNC1, 367 RawContacts.SYNC2, 368 RawContacts.SYNC3, 369 RawContacts.SYNC4, 370 Data.DATA1, 371 Data.DATA2, 372 Data.DATA3, 373 Data.DATA4, 374 Data.DATA5, 375 Data.DATA6, 376 Data.DATA7, 377 Data.DATA8, 378 Data.DATA9, 379 Data.DATA10, 380 Data.DATA11, 381 Data.DATA12, 382 Data.DATA13, 383 Data.DATA14, 384 Data.DATA15, 385 Data.CARRIER_PRESENCE, 386 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, 387 Data.PREFERRED_PHONE_ACCOUNT_ID, 388 Data.DATA_VERSION, 389 Data.IS_PRIMARY, 390 Data.IS_SUPER_PRIMARY, 391 Data.MIMETYPE, 392 Data.RES_PACKAGE, 393 Data.SYNC1, 394 Data.SYNC2, 395 Data.SYNC3, 396 Data.SYNC4, 397 GroupMembership.GROUP_SOURCE_ID, 398 Data.PRESENCE, 399 Data.CHAT_CAPABILITY, 400 Data.STATUS, 401 Data.STATUS_TIMESTAMP, 402 Data.STATUS_RES_PACKAGE, 403 Data.STATUS_LABEL, 404 Data.STATUS_ICON, 405 Data.TIMES_USED, 406 Data.LAST_TIME_USED}, 407 new long[]{contactId} 408 ); 409 } 410 testGetLookupUriByDisplayName()411 public void testGetLookupUriByDisplayName() throws Exception { 412 TestRawContact rawContact = mBuilder.newRawContact() 413 .with(RawContacts.ACCOUNT_TYPE, "test_type") 414 .with(RawContacts.ACCOUNT_NAME, "test_name") 415 .insert(); 416 TestData data = rawContact.newDataRow(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) 417 .with(CommonDataKinds.StructuredName.DISPLAY_NAME, "test name") 418 .insert(); 419 420 Uri lookupUri = Data.getContactLookupUri(mResolver, data.getUri()); 421 assertNotNull("Could not produce a lookup URI", lookupUri); 422 423 TestContact lookupContact = mBuilder.newContact().setUri(lookupUri).load(); 424 assertEquals("Lookup URI matched the wrong contact", 425 lookupContact.getId(), data.load().getRawContact().load().getContactId()); 426 } 427 testContactablesUri()428 public void testContactablesUri() throws Exception { 429 TestRawContact rawContact = mBuilder.newRawContact() 430 .with(RawContacts.ACCOUNT_TYPE, "test_account") 431 .with(RawContacts.ACCOUNT_NAME, "test_name") 432 .insert(); 433 rawContact.newDataRow(CommonDataKinds.Email.CONTENT_ITEM_TYPE) 434 .with(Email.DATA, "test@test.com") 435 .with(Email.TYPE, Email.TYPE_WORK) 436 .insert(); 437 ContentValues cv = new ContentValues(); 438 cv.put(Email.DATA, "test@test.com"); 439 cv.put(Email.TYPE, Email.TYPE_WORK); 440 441 Uri contentUri = ContactsContract.CommonDataKinds.Contactables.CONTENT_URI; 442 try { 443 assertCursorStoredValuesWithRawContactsFilter(contentUri, 444 new long[] {rawContact.getId()}, cv); 445 rawContact.newDataRow(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) 446 .with(CommonDataKinds.StructuredPostal.DATA1, "100 Sesame Street") 447 .insert(); 448 449 rawContact.newDataRow(Phone.CONTENT_ITEM_TYPE) 450 .with(Phone.DATA, "123456789") 451 .with(Phone.TYPE, Phone.TYPE_MOBILE) 452 .insert(); 453 454 ContentValues cv2 = new ContentValues(); 455 cv.put(Phone.DATA, "123456789"); 456 cv.put(Phone.TYPE, Phone.TYPE_MOBILE); 457 458 // Contactables Uri should return only email and phone data items. 459 DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, contentUri, null, 460 Data.RAW_CONTACT_ID + "=?", new String[] {String.valueOf(rawContact.getId())}, 461 null, false, cv, cv2); 462 } finally { 463 // Clean up 464 rawContact.delete(); 465 } 466 } 467 testContactablesFilterByLastName_returnsCorrectDataRows()468 public void testContactablesFilterByLastName_returnsCorrectDataRows() throws Exception { 469 long[] ids = setupContactablesTestData(); 470 Uri filterUri = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale"); 471 assertCursorStoredValuesWithRawContactsFilter(filterUri, ids, 472 ContactablesTestHelper.getContentValues(0)); 473 } 474 testContactablesFilterByFirstName_returnsCorrectDataRows()475 public void testContactablesFilterByFirstName_returnsCorrectDataRows() throws Exception { 476 long[] ids = setupContactablesTestData(); 477 Uri filterUri = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "hot"); 478 assertCursorStoredValuesWithRawContactsFilter(filterUri, ids, 479 ContactablesTestHelper.getContentValues(0)); 480 Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tam"); 481 assertCursorStoredValuesWithRawContactsFilter(filterUri2, ids, 482 ContactablesTestHelper.getContentValues(0, 1)); 483 } 484 testContactablesFilterByPhonePrefix_returnsCorrectDataRows()485 public void testContactablesFilterByPhonePrefix_returnsCorrectDataRows() throws Exception { 486 long[] ids = setupContactablesTestData(); 487 Uri filterUri = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "518"); 488 assertCursorStoredValuesWithRawContactsFilter(filterUri, ids, 489 ContactablesTestHelper.getContentValues(2)); 490 Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "51"); 491 assertCursorStoredValuesWithRawContactsFilter(filterUri2, ids, 492 ContactablesTestHelper.getContentValues(0, 2)); 493 } 494 testContactablesFilterByEmailPrefix_returnsCorrectDataRows()495 public void testContactablesFilterByEmailPrefix_returnsCorrectDataRows() throws Exception { 496 long[] ids = setupContactablesTestData(); 497 Uri filterUri = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "doeassoc"); 498 assertCursorStoredValuesWithRawContactsFilter(filterUri, ids, 499 ContactablesTestHelper.getContentValues(2)); 500 } 501 testContactablesFilter_doesNotExist_returnsCorrectDataRows()502 public void testContactablesFilter_doesNotExist_returnsCorrectDataRows() throws Exception { 503 long[] ids = setupContactablesTestData(); 504 Uri filterUri = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "doesnotexist"); 505 assertCursorStoredValuesWithRawContactsFilter(filterUri, ids, new ContentValues[0]); 506 } 507 508 /** 509 * Verifies that Callable.CONTENT_URI returns only data items that can be called (i.e. 510 * phone numbers and sip addresses) 511 */ testCallableUri_returnsCorrectDataRows()512 public void testCallableUri_returnsCorrectDataRows() throws Exception { 513 long[] ids = setupContactablesTestData(); 514 Uri uri = Callable.CONTENT_URI; 515 assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1], 516 sContentValues[5], sContentValues[6]); 517 } 518 testCallableFilterByNameOrOrganization_returnsCorrectDataRows()519 public void testCallableFilterByNameOrOrganization_returnsCorrectDataRows() throws Exception { 520 long[] ids = setupContactablesTestData(); 521 Uri uri = Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, "doe"); 522 // Only callables belonging to John Doe (name) and Cold Tamago (organization) are returned. 523 assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[5], 524 sContentValues[6]); 525 } 526 testCallableFilterByNumber_returnsCorrectDataRows()527 public void testCallableFilterByNumber_returnsCorrectDataRows() throws Exception { 528 long[] ids = setupContactablesTestData(); 529 Uri uri = Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, "510"); 530 assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1]); 531 } 532 testCallableFilterBySipAddress_returnsCorrectDataRows()533 public void testCallableFilterBySipAddress_returnsCorrectDataRows() throws Exception { 534 long[] ids = setupContactablesTestData(); 535 Uri uri = Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, "mysip"); 536 assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[6]); 537 } 538 testEnterpriseCallableFilterByNameOrOrganization_returnsCorrectDataRows()539 public void testEnterpriseCallableFilterByNameOrOrganization_returnsCorrectDataRows() 540 throws Exception { 541 long[] ids = setupContactablesTestData(); 542 Uri uri = Uri.withAppendedPath(Callable.ENTERPRISE_CONTENT_FILTER_URI, "doe").buildUpon() 543 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, 544 String.valueOf(Directory.DEFAULT)) 545 .build(); 546 // Only callables belonging to John Doe (name) and Cold Tamago (organization) are returned. 547 assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[5], 548 sContentValues[6]); 549 } 550 testEnterpriseCallableFilterByNumber_returnsCorrectDataRows()551 public void testEnterpriseCallableFilterByNumber_returnsCorrectDataRows() throws Exception { 552 long[] ids = setupContactablesTestData(); 553 Uri uri = Uri.withAppendedPath(Callable.ENTERPRISE_CONTENT_FILTER_URI, "510").buildUpon() 554 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, 555 String.valueOf(Directory.DEFAULT)) 556 .build(); 557 assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1]); 558 } 559 testEnterpriseCallableFilterBySipAddress_returnsCorrectDataRows()560 public void testEnterpriseCallableFilterBySipAddress_returnsCorrectDataRows() 561 throws Exception { 562 long[] ids = setupContactablesTestData(); 563 Uri uri = Uri.withAppendedPath(Callable.ENTERPRISE_CONTENT_FILTER_URI, "mysip").buildUpon() 564 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, 565 String.valueOf(Directory.DEFAULT)) 566 .build(); 567 assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[6]); 568 } 569 testDataInsert_updatesContactLastUpdatedTimestamp()570 public void testDataInsert_updatesContactLastUpdatedTimestamp() { 571 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 572 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 573 574 SystemClock.sleep(1); 575 createData(ids.mRawContactId); 576 577 long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 578 assertTrue(newTime > baseTime); 579 580 // Clean up 581 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 582 } 583 testDataDelete_updatesContactLastUpdatedTimestamp()584 public void testDataDelete_updatesContactLastUpdatedTimestamp() { 585 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 586 587 long dataId = createData(ids.mRawContactId); 588 589 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 590 591 SystemClock.sleep(1); 592 DataUtil.delete(mResolver, dataId); 593 594 long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 595 assertTrue(newTime > baseTime); 596 597 // Clean up 598 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 599 } 600 601 /** 602 * Tests that specifying the {@link android.provider.ContactsContract#REMOVE_DUPLICATE_ENTRIES} 603 * boolean parameter correctly results in deduped phone numbers. 604 */ testPhoneQuery_removeDuplicateEntries()605 public void testPhoneQuery_removeDuplicateEntries() throws Exception{ 606 long[] ids = setupContactablesTestData(); 607 608 // Insert duplicate data entry for raw contact 3. (existing phone number 518-354-1111) 609 mRawContacts[2].newDataRow(Phone.CONTENT_ITEM_TYPE) 610 .with(Phone.DATA, "518-354-1111") 611 .with(Phone.TYPE, Phone.TYPE_HOME) 612 .insert(); 613 614 ContentValues dupe = new ContentValues(); 615 dupe.put(Contacts.DISPLAY_NAME, "John Doe"); 616 dupe.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 617 dupe.put(Phone.DATA, "518-354-1111"); 618 dupe.put(Phone.TYPE, Phone.TYPE_HOME); 619 620 // Query for all phone numbers in the contacts database (without deduping). 621 // The phone number above should be listed twice, in its duplicated forms. 622 assertCursorStoredValuesWithRawContactsFilter(Phone.CONTENT_URI, ids, sContentValues[1], 623 sContentValues[5], dupe); 624 625 // Now query for all phone numbers in the contacts database but request deduping. 626 // The phone number should now be listed only once. 627 Uri uri = Phone.CONTENT_URI.buildUpon(). 628 appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").build(); 629 assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1], 630 sContentValues[5]); 631 } 632 633 /** 634 * Tests that specifying the {@link android.provider.ContactsContract#REMOVE_DUPLICATE_ENTRIES} 635 * boolean parameter correctly results in deduped email addresses. 636 */ testEmailQuery_removeDuplicateEntries()637 public void testEmailQuery_removeDuplicateEntries() throws Exception{ 638 long[] ids = setupContactablesTestData(); 639 640 // Insert duplicate data entry for raw contact 3. (existing email doeassociates@deer.com) 641 mRawContacts[2].newDataRow(Email.CONTENT_ITEM_TYPE) 642 .with(Email.DATA, "doeassociates@deer.com") 643 .with(Email.TYPE, Email.TYPE_WORK) 644 .insert(); 645 646 ContentValues dupe = new ContentValues(); 647 dupe.put(Contacts.DISPLAY_NAME, "John Doe"); 648 dupe.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 649 dupe.put(Email.DATA, "doeassociates@deer.com"); 650 dupe.put(Email.TYPE, Email.TYPE_WORK); 651 652 // Query for all email addresses in the contacts database (without deduping). 653 // The email address above should be listed twice, in its duplicated forms. 654 assertCursorStoredValuesWithRawContactsFilter(Email.CONTENT_URI, ids, sContentValues[0], 655 sContentValues[2], sContentValues[3], sContentValues[4], dupe); 656 657 // Now query for all email addresses in the contacts database but request deduping. 658 // The email address should now be listed only once. 659 Uri uri = Email.CONTENT_URI.buildUpon(). 660 appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").build(); 661 assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[0], 662 sContentValues[2], sContentValues[3], sContentValues[4]); 663 } 664 testDataUpdate_updatesContactLastUpdatedTimestamp()665 public void testDataUpdate_updatesContactLastUpdatedTimestamp() { 666 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 667 long dataId = createData(ids.mRawContactId); 668 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 669 670 SystemClock.sleep(1); 671 ContentValues values = new ContentValues(); 672 values.put(CommonDataKinds.Phone.NUMBER, "555-5555"); 673 DataUtil.update(mResolver, dataId, values); 674 675 long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 676 assertTrue("Expected contact " + ids.mContactId + " last updated to be greater than " + 677 baseTime + ". But was " + newTime, newTime > baseTime); 678 679 // Clean up 680 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 681 } 682 createData(long rawContactId)683 private long createData(long rawContactId) { 684 ContentValues values = new ContentValues(); 685 values.put(Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE); 686 values.put(CommonDataKinds.Phone.NUMBER, "1-800-GOOG-411"); 687 values.put(CommonDataKinds.Phone.TYPE, CommonDataKinds.Phone.TYPE_CUSTOM); 688 values.put(CommonDataKinds.Phone.LABEL, "free directory assistance"); 689 return DataUtil.insertData(mResolver, rawContactId, values); 690 } 691 assertCursorStoredValuesWithRawContactsFilter(Uri uri, long[] rawContactsId, ContentValues... expected)692 private void assertCursorStoredValuesWithRawContactsFilter(Uri uri, long[] rawContactsId, 693 ContentValues... expected) { 694 // We need this helper function to add a filter for specific raw contacts because 695 // otherwise tests will fail if performed on a device with existing contacts data 696 StringBuilder sb = new StringBuilder(); 697 sb.append(Data.RAW_CONTACT_ID + " in "); 698 sb.append("("); 699 for (int i = 0; i < rawContactsId.length; i++) { 700 if (i != 0) sb.append(","); 701 sb.append(rawContactsId[i]); 702 } 703 sb.append(")"); 704 DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, uri, null, sb.toString(), 705 null, null, false, expected); 706 } 707 708 setupContactablesTestData()709 private long[] setupContactablesTestData() throws Exception { 710 TestRawContact rawContact = mBuilder.newRawContact() 711 .with(RawContacts.ACCOUNT_TYPE, "test_account") 712 .with(RawContacts.ACCOUNT_NAME, "test_name") 713 .insert(); 714 rawContact.newDataRow(StructuredName.CONTENT_ITEM_TYPE) 715 .with(StructuredName.DISPLAY_NAME, "Hot Tamale") 716 .insert(); 717 rawContact.newDataRow(Email.CONTENT_ITEM_TYPE) 718 .with(Email.DATA, "tamale@acme.com") 719 .with(Email.TYPE, Email.TYPE_HOME) 720 .insert(); 721 rawContact.newDataRow(Email.CONTENT_ITEM_TYPE) 722 .with(Email.DATA, "hot@google.com") 723 .with(Email.TYPE, Email.TYPE_WORK) 724 .insert(); 725 rawContact.newDataRow(Phone.CONTENT_ITEM_TYPE) 726 .with(Phone.DATA, "510-123-5769") 727 .with(Email.TYPE, Phone.TYPE_HOME) 728 .insert(); 729 mRawContacts[0] = rawContact; 730 731 TestRawContact rawContact2 = mBuilder.newRawContact() 732 .with(RawContacts.ACCOUNT_TYPE, "test_account") 733 .with(RawContacts.ACCOUNT_NAME, "test_name") 734 .insert(); 735 rawContact2.newDataRow(StructuredName.CONTENT_ITEM_TYPE) 736 .with(StructuredName.DISPLAY_NAME, "Cold Tamago") 737 .insert(); 738 rawContact2.newDataRow(Email.CONTENT_ITEM_TYPE) 739 .with(Email.DATA, "eggs@farmers.org") 740 .with(Email.TYPE, Email.TYPE_HOME) 741 .insert(); 742 rawContact2.newDataRow(SipAddress.CONTENT_ITEM_TYPE) 743 .with(SipAddress.DATA, "mysip@sipaddress.com") 744 .with(SipAddress.TYPE, SipAddress.TYPE_HOME) 745 .insert(); 746 rawContact2.newDataRow(Organization.CONTENT_ITEM_TYPE) 747 .with(Organization.COMPANY, "Doe Corp") 748 .insert(); 749 mRawContacts[1] = rawContact2; 750 751 TestRawContact rawContact3 = mBuilder.newRawContact() 752 .with(RawContacts.ACCOUNT_TYPE, "test_account") 753 .with(RawContacts.ACCOUNT_NAME, "test_name") 754 .insert(); 755 rawContact3.newDataRow(StructuredName.CONTENT_ITEM_TYPE) 756 .with(StructuredName.DISPLAY_NAME, "John Doe") 757 .insert(); 758 rawContact3.newDataRow(Email.CONTENT_ITEM_TYPE) 759 .with(Email.DATA, "doeassociates@deer.com") 760 .with(Email.TYPE, Email.TYPE_WORK) 761 .insert(); 762 rawContact3.newDataRow(Phone.CONTENT_ITEM_TYPE) 763 .with(Phone.DATA, "518-354-1111") 764 .with(Phone.TYPE, Phone.TYPE_HOME) 765 .insert(); 766 rawContact3.newDataRow(Organization.CONTENT_ITEM_TYPE) 767 .with(Organization.DATA, "Doe Industries") 768 .insert(); 769 mRawContacts[2] = rawContact3; 770 return new long[] {rawContact.getId(), rawContact2.getId(), rawContact3.getId()}; 771 } 772 773 // Provides functionality to set up content values for the Contactables tests 774 private static class ContactablesTestHelper { 775 776 /** 777 * @return An arraylist of contentValues that correspond to the provided raw contacts 778 */ getContentValues(int... rawContacts)779 public static ContentValues[] getContentValues(int... rawContacts) { 780 ArrayList<ContentValues> cv = new ArrayList<ContentValues>(); 781 for (int i = 0; i < rawContacts.length; i++) { 782 switch (rawContacts[i]) { 783 case 0: 784 // rawContact 0 "Hot Tamale" contains ContentValues 0, 1, and 2 785 cv.add(sContentValues[0]); 786 cv.add(sContentValues[1]); 787 cv.add(sContentValues[2]); 788 break; 789 case 1: 790 // rawContact 1 "Cold Tamago" contains ContentValues 3 791 cv.add(sContentValues[3]); 792 break; 793 case 2: 794 // rawContact 1 "John Doe" contains ContentValues 4, 5 795 cv.add(sContentValues[4]); 796 cv.add(sContentValues[5]); 797 break; 798 } 799 } 800 ContentValues[] toReturn = new ContentValues[cv.size()]; 801 for (int i = 0; i < cv.size(); i++) { 802 toReturn[i] = cv.get(i); 803 } 804 return toReturn; 805 } 806 } 807 } 808 809