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