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 com.google.android.collect.Lists; 20 21 import android.accounts.Account; 22 import android.content.ContentProviderOperation; 23 import android.content.ContentProviderResult; 24 import android.content.ContentUris; 25 import android.content.ContentValues; 26 import android.database.Cursor; 27 import android.net.Uri; 28 import android.provider.ContactsContract; 29 import android.provider.ContactsContract.AggregationExceptions; 30 import android.provider.ContactsContract.Contacts; 31 import android.provider.ContactsContract.Data; 32 import android.provider.ContactsContract.RawContacts; 33 import android.provider.ContactsContract.CommonDataKinds.Organization; 34 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 35 import android.test.suitebuilder.annotation.LargeTest; 36 37 /** 38 * Unit tests for {@link ContactAggregator}. 39 * 40 * Run the test like this: 41 * <code> 42 * adb shell am instrument -e class com.android.providers.contacts.ContactAggregatorTest -w \ 43 * com.android.providers.contacts.tests/android.test.InstrumentationTestRunner 44 * </code> 45 */ 46 @LargeTest 47 public class ContactAggregatorTest extends BaseContactsProvider2Test { 48 49 private static final Account ACCOUNT_1 = new Account("account_name_1", "account_type_1"); 50 private static final Account ACCOUNT_2 = new Account("account_name_2", "account_type_2"); 51 private static final Account ACCOUNT_3 = new Account("account_name_3", "account_type_3"); 52 53 private static final String[] AGGREGATION_EXCEPTION_PROJECTION = new String[] { 54 AggregationExceptions.TYPE, 55 AggregationExceptions.RAW_CONTACT_ID1, 56 AggregationExceptions.RAW_CONTACT_ID2 57 }; 58 59 public void testCrudAggregationExceptions() throws Exception { 60 long rawContactId1 = createRawContactWithName("zz", "top"); 61 long rawContactId2 = createRawContactWithName("aa", "bottom"); 62 63 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 64 rawContactId1, rawContactId2); 65 66 String selection = "(" + AggregationExceptions.RAW_CONTACT_ID1 + "=" + rawContactId1 67 + " AND " + AggregationExceptions.RAW_CONTACT_ID2 + "=" + rawContactId2 68 + ") OR (" + AggregationExceptions.RAW_CONTACT_ID1 + "=" + rawContactId2 69 + " AND " + AggregationExceptions.RAW_CONTACT_ID2 + "=" + rawContactId1 + ")"; 70 71 // Refetch the row we have just inserted 72 Cursor c = mResolver.query(AggregationExceptions.CONTENT_URI, 73 AGGREGATION_EXCEPTION_PROJECTION, selection, null, null); 74 75 assertTrue(c.moveToFirst()); 76 assertEquals(AggregationExceptions.TYPE_KEEP_TOGETHER, c.getInt(0)); 77 assertTrue((rawContactId1 == c.getLong(1) && rawContactId2 == c.getLong(2)) 78 || (rawContactId2 == c.getLong(1) && rawContactId1 == c.getLong(2))); 79 assertFalse(c.moveToNext()); 80 c.close(); 81 82 // Change from TYPE_KEEP_IN to TYPE_KEEP_OUT 83 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 84 rawContactId1, rawContactId2); 85 86 c = mResolver.query(AggregationExceptions.CONTENT_URI, AGGREGATION_EXCEPTION_PROJECTION, 87 selection, null, null); 88 89 assertTrue(c.moveToFirst()); 90 assertEquals(AggregationExceptions.TYPE_KEEP_SEPARATE, c.getInt(0)); 91 assertTrue((rawContactId1 == c.getLong(1) && rawContactId2 == c.getLong(2)) 92 || (rawContactId2 == c.getLong(1) && rawContactId1 == c.getLong(2))); 93 assertFalse(c.moveToNext()); 94 c.close(); 95 96 // Delete the rule 97 setAggregationException(AggregationExceptions.TYPE_AUTOMATIC, 98 rawContactId1, rawContactId2); 99 100 // Verify that the row is gone 101 c = mResolver.query(AggregationExceptions.CONTENT_URI, AGGREGATION_EXCEPTION_PROJECTION, 102 selection, null, null); 103 assertFalse(c.moveToFirst()); 104 c.close(); 105 } 106 107 public void testAggregationCreatesNewAggregate() { 108 long rawContactId = createRawContact(); 109 110 Uri resultUri = insertStructuredName(rawContactId, "Johna", "Smitha"); 111 112 // Parse the URI and confirm that it contains an ID 113 assertTrue(ContentUris.parseId(resultUri) != 0); 114 115 long contactId = queryContactId(rawContactId); 116 assertTrue(contactId != 0); 117 118 String displayName = queryDisplayName(contactId); 119 assertEquals("Johna Smitha", displayName); 120 } 121 122 public void testNonAggregationFromSameAccount() { 123 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 124 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 125 assertNotAggregated(rawContactId1, rawContactId2); 126 } 127 128 public void testAggregationOfExactFullNameMatch() { 129 long rawContactId1 = createRawContact(ACCOUNT_1); 130 insertStructuredName(rawContactId1, "Johnb", "Smithb"); 131 132 long rawContactId2 = createRawContact(ACCOUNT_2); 133 insertStructuredName(rawContactId2, "Johnb", "Smithb"); 134 135 assertAggregated(rawContactId1, rawContactId2, "Johnb Smithb"); 136 } 137 138 public void testAggregationOfCaseInsensitiveFullNameMatch() { 139 long rawContactId1 = createRawContact(ACCOUNT_1); 140 insertStructuredName(rawContactId1, "Johnc", "Smithc"); 141 142 long rawContactId2 = createRawContact(ACCOUNT_2); 143 insertStructuredName(rawContactId2, "Johnc", "smithc"); 144 145 assertAggregated(rawContactId1, rawContactId2, "Johnc Smithc"); 146 } 147 148 public void testAggregationOfLastNameMatch() { 149 long rawContactId1 = createRawContact(ACCOUNT_1); 150 insertStructuredName(rawContactId1, null, "Johnd"); 151 152 long rawContactId2 = createRawContact(ACCOUNT_2); 153 insertStructuredName(rawContactId2, null, "johnd"); 154 155 assertAggregated(rawContactId1, rawContactId2, "Johnd"); 156 } 157 158 public void testNonAggregationOfFirstNameMatch() { 159 long rawContactId1 = createRawContact(ACCOUNT_1); 160 insertStructuredName(rawContactId1, "Johne", "Smithe"); 161 162 long rawContactId2 = createRawContact(ACCOUNT_2); 163 insertStructuredName(rawContactId2, "Johne", null); 164 165 assertNotAggregated(rawContactId1, rawContactId2); 166 } 167 168 // TODO: should this be allowed to match? 169 public void testNonAggregationOfLastNameMatch() { 170 long rawContactId1 = createRawContact(ACCOUNT_1); 171 insertStructuredName(rawContactId1, "Johnf", "Smithf"); 172 173 long rawContactId2 = createRawContact(ACCOUNT_2); 174 insertStructuredName(rawContactId2, null, "Smithf"); 175 176 assertNotAggregated(rawContactId1, rawContactId2); 177 } 178 179 public void testAggregationOfConcatenatedFullNameMatch() { 180 long rawContactId1 = createRawContact(ACCOUNT_1); 181 insertStructuredName(rawContactId1, "Johng", "Smithg"); 182 183 long rawContactId2 = createRawContact(ACCOUNT_2); 184 insertStructuredName(rawContactId2, "johngsmithg", null); 185 186 assertAggregated(rawContactId1, rawContactId2, "Johng Smithg"); 187 } 188 189 public void testAggregationOfNormalizedFullNameMatch() { 190 long rawContactId1 = createRawContact(ACCOUNT_1); 191 insertStructuredName(rawContactId1, "H\u00e9l\u00e8ne", "Bj\u00f8rn"); 192 193 long rawContactId2 = createRawContact(ACCOUNT_2); 194 insertStructuredName(rawContactId2, "helene bjorn", null); 195 196 assertAggregated(rawContactId1, rawContactId2, "H\u00e9l\u00e8ne Bj\u00f8rn"); 197 } 198 199 public void testAggregationOfNormalizedFullNameMatchWithReadOnlyAccount() { 200 long rawContactId1 = createRawContact(new Account("acct", READ_ONLY_ACCOUNT_TYPE)); 201 insertStructuredName(rawContactId1, "H\u00e9l\u00e8ne", "Bj\u00f8rn"); 202 203 long rawContactId2 = createRawContact(); 204 insertStructuredName(rawContactId2, "helene bjorn", null); 205 206 assertAggregated(rawContactId1, rawContactId2, "helene bjorn"); 207 } 208 209 public void testAggregationOfNumericNames() { 210 long rawContactId1 = createRawContact(ACCOUNT_1); 211 insertStructuredName(rawContactId1, "123", null); 212 213 long rawContactId2 = createRawContact(ACCOUNT_2); 214 insertStructuredName(rawContactId2, "1-2-3", null); 215 216 assertAggregated(rawContactId1, rawContactId2, "1-2-3"); 217 } 218 219 public void testAggregationOfInconsistentlyParsedNames() { 220 long rawContactId1 = createRawContact(ACCOUNT_1); 221 222 ContentValues values = new ContentValues(); 223 values.put(StructuredName.DISPLAY_NAME, "604 Arizona Ave"); 224 values.put(StructuredName.GIVEN_NAME, "604"); 225 values.put(StructuredName.MIDDLE_NAME, "Arizona"); 226 values.put(StructuredName.FAMILY_NAME, "Ave"); 227 insertStructuredName(rawContactId1, values); 228 229 long rawContactId2 = createRawContact(ACCOUNT_2); 230 values.clear(); 231 values.put(StructuredName.DISPLAY_NAME, "604 Arizona Ave"); 232 values.put(StructuredName.GIVEN_NAME, "604"); 233 values.put(StructuredName.FAMILY_NAME, "Arizona Ave"); 234 insertStructuredName(rawContactId2, values); 235 236 assertAggregated(rawContactId1, rawContactId2, "604 Arizona Ave"); 237 } 238 239 public void testAggregationBasedOnMiddleName() { 240 ContentValues values = new ContentValues(); 241 long rawContactId1 = createRawContact(ACCOUNT_1); 242 values.put(StructuredName.GIVEN_NAME, "John"); 243 values.put(StructuredName.GIVEN_NAME, "Abigale"); 244 values.put(StructuredName.FAMILY_NAME, "James"); 245 246 insertStructuredName(rawContactId1, values); 247 248 long rawContactId2 = createRawContact(ACCOUNT_2); 249 values.clear(); 250 values.put(StructuredName.GIVEN_NAME, "John"); 251 values.put(StructuredName.GIVEN_NAME, "Marie"); 252 values.put(StructuredName.FAMILY_NAME, "James"); 253 insertStructuredName(rawContactId2, values); 254 255 assertNotAggregated(rawContactId1, rawContactId2); 256 } 257 258 public void testAggregationBasedOnPhoneNumberNoNameData() { 259 long rawContactId1 = createRawContact(ACCOUNT_1); 260 insertPhoneNumber(rawContactId1, "(888)555-1231"); 261 262 long rawContactId2 = createRawContact(ACCOUNT_2); 263 insertPhoneNumber(rawContactId2, "1(888)555-1231"); 264 265 assertAggregated(rawContactId1, rawContactId2); 266 } 267 268 public void testNonAggregationBasedOnPhoneNumberNoNameDataSameAccount() { 269 long rawContactId1 = createRawContact(ACCOUNT_1); 270 insertPhoneNumber(rawContactId1, "(888)555-1231"); 271 272 long rawContactId2 = createRawContact(ACCOUNT_2); 273 insertPhoneNumber(rawContactId2, "1(888)555-1231"); 274 275 long rawContactId3 = createRawContact(ACCOUNT_1); 276 insertPhoneNumber(rawContactId3, "888-555-1231"); 277 278 assertNotAggregated(rawContactId1, rawContactId2); 279 assertNotAggregated(rawContactId1, rawContactId3); 280 assertNotAggregated(rawContactId2, rawContactId3); 281 } 282 283 public void testAggregationBasedOnPhoneNumberWhenTargetAggregateHasNoName() { 284 long rawContactId1 = createRawContact(ACCOUNT_1); 285 insertPhoneNumber(rawContactId1, "(888)555-1232"); 286 287 long rawContactId2 = createRawContact(ACCOUNT_2); 288 insertStructuredName(rawContactId2, "Johnl", "Smithl"); 289 insertPhoneNumber(rawContactId2, "1(888)555-1232"); 290 291 assertAggregated(rawContactId1, rawContactId2); 292 } 293 294 public void testAggregationBasedOnPhoneNumberWhenNewContactHasNoName() { 295 long rawContactId1 = createRawContact(ACCOUNT_1); 296 insertStructuredName(rawContactId1, "Johnm", "Smithm"); 297 insertPhoneNumber(rawContactId1, "(888)555-1233"); 298 299 long rawContactId2 = createRawContact(ACCOUNT_2); 300 insertPhoneNumber(rawContactId2, "1(888)555-1233"); 301 302 assertAggregated(rawContactId1, rawContactId2); 303 } 304 305 public void testAggregationBasedOnPhoneNumberWithDifferentNames() { 306 long rawContactId1 = createRawContact(ACCOUNT_1); 307 insertStructuredName(rawContactId1, "Baby", "Bear"); 308 insertPhoneNumber(rawContactId1, "(888)555-1235"); 309 310 long rawContactId2 = createRawContact(ACCOUNT_2); 311 insertStructuredName(rawContactId2, "Blind", "Mouse"); 312 insertPhoneNumber(rawContactId2, "1(888)555-1235"); 313 314 assertNotAggregated(rawContactId1, rawContactId2); 315 } 316 317 public void testAggregationBasedOnPhoneNumberWithJustFirstName() { 318 long rawContactId1 = createRawContact(ACCOUNT_1); 319 insertStructuredName(rawContactId1, "Chick", "Notnull"); 320 insertPhoneNumber(rawContactId1, "(888)555-1236"); 321 322 long rawContactId2 = createRawContact(ACCOUNT_2); 323 insertStructuredName(rawContactId2, "Chick", null); 324 insertPhoneNumber(rawContactId2, "1(888)555-1236"); 325 326 assertAggregated(rawContactId1, rawContactId2); 327 } 328 329 public void testAggregationBasedOnEmailNoNameData() { 330 long rawContactId1 = createRawContact(ACCOUNT_1); 331 insertEmail(rawContactId1, "lightning (at) android.com"); 332 333 long rawContactId2 = createRawContact(ACCOUNT_2); 334 insertEmail(rawContactId2, "lightning (at) android.com"); 335 336 assertAggregated(rawContactId1, rawContactId2); 337 } 338 339 public void testAggregationBasedOnEmailWhenTargetAggregateHasNoName() { 340 long rawContactId1 = createRawContact(ACCOUNT_1); 341 insertEmail(rawContactId1, "mcqueen (at) android.com"); 342 343 long rawContactId2 = createRawContact(ACCOUNT_2); 344 insertStructuredName(rawContactId2, "Lightning", "McQueen"); 345 insertEmail(rawContactId2, "mcqueen (at) android.com"); 346 347 assertAggregated(rawContactId1, rawContactId2, "Lightning McQueen"); 348 } 349 350 public void testAggregationBasedOnEmailWhenNewContactHasNoName() { 351 long rawContactId1 = createRawContact(ACCOUNT_1); 352 insertStructuredName(rawContactId1, "Doc", "Hudson"); 353 insertEmail(rawContactId1, "doc (at) android.com"); 354 355 long rawContactId2 = createRawContact(ACCOUNT_2); 356 insertEmail(rawContactId2, "doc (at) android.com"); 357 358 assertAggregated(rawContactId1, rawContactId2); 359 } 360 361 public void testAggregationBasedOnEmailWithDifferentNames() { 362 long rawContactId1 = createRawContact(ACCOUNT_1); 363 insertStructuredName(rawContactId1, "Chick", "Hicks"); 364 insertEmail(rawContactId1, "hicky (at) android.com"); 365 366 long rawContactId2 = createRawContact(ACCOUNT_2); 367 insertStructuredName(rawContactId2, "Luigi", "Guido"); 368 insertEmail(rawContactId2, "hicky (at) android.com"); 369 370 assertNotAggregated(rawContactId1, rawContactId2); 371 } 372 373 public void testAggregationByCommonNicknameWithLastName() { 374 long rawContactId1 = createRawContact(ACCOUNT_1); 375 insertStructuredName(rawContactId1, "Bill", "Gore"); 376 377 long rawContactId2 = createRawContact(ACCOUNT_2); 378 insertStructuredName(rawContactId2, "William", "Gore"); 379 380 assertAggregated(rawContactId1, rawContactId2, "William Gore"); 381 } 382 383 public void testAggregationByCommonNicknameOnly() { 384 long rawContactId1 = createRawContact(ACCOUNT_1); 385 insertStructuredName(rawContactId1, "Lawrence", null); 386 387 long rawContactId2 = createRawContact(ACCOUNT_2); 388 insertStructuredName(rawContactId2, "Larry", null); 389 390 assertAggregated(rawContactId1, rawContactId2, "Lawrence"); 391 } 392 393 public void testAggregationByNicknameNoStructuredName() { 394 long rawContactId1 = createRawContact(ACCOUNT_1); 395 insertNickname(rawContactId1, "Frozone"); 396 397 long rawContactId2 = createRawContact(ACCOUNT_2); 398 insertNickname(rawContactId2, "Frozone"); 399 400 assertAggregated(rawContactId1, rawContactId2); 401 } 402 403 public void testAggregationByNicknameWithDifferentNames() { 404 long rawContactId1 = createRawContact(ACCOUNT_1); 405 insertStructuredName(rawContactId1, "Helen", "Parr"); 406 insertNickname(rawContactId1, "Elastigirl"); 407 408 long rawContactId2 = createRawContact(ACCOUNT_2); 409 insertStructuredName(rawContactId2, "Shawn", "Johnson"); 410 insertNickname(rawContactId2, "Elastigirl"); 411 412 assertNotAggregated(rawContactId1, rawContactId2); 413 } 414 415 public void testNonAggregationOnOrganization() { 416 ContentValues values = new ContentValues(); 417 values.put(Organization.TITLE, "Monsters, Inc"); 418 long rawContactId1 = createRawContact(ACCOUNT_1); 419 insertOrganization(rawContactId1, values); 420 insertNickname(rawContactId1, "Boo"); 421 422 long rawContactId2 = createRawContact(ACCOUNT_2); 423 insertOrganization(rawContactId2, values); 424 insertNickname(rawContactId2, "Rendall"); // To force reaggregation 425 426 assertNotAggregated(rawContactId1, rawContactId2); 427 } 428 429 public void testAggregationExceptionKeepIn() { 430 long rawContactId1 = createRawContact(ACCOUNT_1); 431 insertStructuredName(rawContactId1, "Johnk", "Smithk"); 432 433 long rawContactId2 = createRawContact(ACCOUNT_2); 434 insertStructuredName(rawContactId2, "Johnkx", "Smithkx"); 435 436 long contactId1 = queryContactId(rawContactId1); 437 long contactId2 = queryContactId(rawContactId2); 438 439 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 440 rawContactId1, rawContactId2); 441 442 assertAggregated(rawContactId1, rawContactId2, "Johnkx Smithkx"); 443 444 // Assert that the empty aggregate got removed 445 long newContactId1 = queryContactId(rawContactId1); 446 if (contactId1 != newContactId1) { 447 Cursor cursor = queryContact(contactId1); 448 assertFalse(cursor.moveToFirst()); 449 cursor.close(); 450 } else { 451 Cursor cursor = queryContact(contactId2); 452 assertFalse(cursor.moveToFirst()); 453 cursor.close(); 454 } 455 } 456 457 public void testAggregationExceptionKeepOut() { 458 long rawContactId1 = createRawContact(ACCOUNT_1); 459 insertStructuredName(rawContactId1, "Johnh", "Smithh"); 460 461 long rawContactId2 = createRawContact(ACCOUNT_2); 462 insertStructuredName(rawContactId2, "Johnh", "Smithh"); 463 464 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 465 rawContactId1, rawContactId2); 466 467 assertNotAggregated(rawContactId1, rawContactId2); 468 } 469 470 public void testAggregationExceptionKeepOutCheckUpdatesDisplayName() { 471 long rawContactId1 = createRawContact(ACCOUNT_1); 472 insertStructuredName(rawContactId1, "Johni", "Smithi"); 473 474 long rawContactId2 = createRawContact(ACCOUNT_2); 475 insertStructuredName(rawContactId2, "Johnj", "Smithj"); 476 477 long rawContactId3 = createRawContact(ACCOUNT_3); 478 insertStructuredName(rawContactId3, "Johnm", "Smithm"); 479 480 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 481 rawContactId1, rawContactId2); 482 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 483 rawContactId1, rawContactId3); 484 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 485 rawContactId2, rawContactId3); 486 487 assertAggregated(rawContactId1, rawContactId2, "Johnm Smithm"); 488 assertAggregated(rawContactId1, rawContactId3); 489 490 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 491 rawContactId1, rawContactId2); 492 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 493 rawContactId1, rawContactId3); 494 495 assertNotAggregated(rawContactId1, rawContactId2); 496 assertNotAggregated(rawContactId1, rawContactId3); 497 498 String displayName1 = queryDisplayName(queryContactId(rawContactId1)); 499 assertEquals("Johni Smithi", displayName1); 500 501 assertAggregated(rawContactId2, rawContactId3, "Johnm Smithm"); 502 503 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 504 rawContactId2, rawContactId3); 505 assertNotAggregated(rawContactId1, rawContactId2); 506 assertNotAggregated(rawContactId1, rawContactId3); 507 assertNotAggregated(rawContactId2, rawContactId3); 508 509 String displayName2 = queryDisplayName(queryContactId(rawContactId1)); 510 assertEquals("Johni Smithi", displayName2); 511 512 String displayName3 = queryDisplayName(queryContactId(rawContactId2)); 513 assertEquals("Johnj Smithj", displayName3); 514 515 String displayName4 = queryDisplayName(queryContactId(rawContactId3)); 516 assertEquals("Johnm Smithm", displayName4); 517 } 518 519 public void testNonAggregationWithMultipleAffinities() { 520 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 521 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 522 assertNotAggregated(rawContactId1, rawContactId2); 523 524 // There are two aggregates this raw contact could join, so it should join neither 525 long rawContactId3 = createRawContactWithName("John", "Doe", ACCOUNT_2); 526 assertNotAggregated(rawContactId1, rawContactId3); 527 assertNotAggregated(rawContactId2, rawContactId3); 528 529 // Just in case - let's make sure the original two did not get aggregated in the process 530 assertNotAggregated(rawContactId1, rawContactId2); 531 } 532 533 public void testSplitBecauseOfMultipleAffinities() { 534 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 535 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_2); 536 assertAggregated(rawContactId1, rawContactId2); 537 538 // The aggregate this raw contact could join has a raw contact from the same account, 539 // let's not aggregate and break up the existing aggregate because of the ambiguity 540 long rawContactId3 = createRawContactWithName("John", "Doe", ACCOUNT_1); 541 assertNotAggregated(rawContactId1, rawContactId3); 542 assertNotAggregated(rawContactId2, rawContactId3); 543 assertNotAggregated(rawContactId1, rawContactId2); 544 } 545 546 public void testNonSplitBecauseOfMultipleAffinitiesWhenOverridden() { 547 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 548 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_2); 549 long rawContactId3 = createRawContactWithName("John", "Doe", ACCOUNT_3); 550 assertAggregated(rawContactId1, rawContactId2); 551 assertAggregated(rawContactId1, rawContactId3); 552 setAggregationException( 553 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 554 assertAggregated(rawContactId1, rawContactId2); 555 assertAggregated(rawContactId1, rawContactId3); 556 557 // The aggregate this raw contact could join has a raw contact from the same account, 558 // let's not aggregate and break up the existing aggregate because of the ambiguity 559 long rawContactId4 = createRawContactWithName("John", "Doe", ACCOUNT_1); 560 assertAggregated(rawContactId1, rawContactId2); // Aggregation exception 561 assertNotAggregated(rawContactId1, rawContactId3); 562 assertNotAggregated(rawContactId1, rawContactId4); 563 assertNotAggregated(rawContactId3, rawContactId4); 564 } 565 566 public void testAggregationSuggestionsBasedOnName() { 567 long rawContactId1 = createRawContact(); 568 insertStructuredName(rawContactId1, "Duane", null); 569 570 // Exact name match 571 long rawContactId2 = createRawContact(); 572 insertStructuredName(rawContactId2, "Duane", null); 573 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 574 rawContactId1, rawContactId2); 575 576 // Edit distance == 0.84 577 long rawContactId3 = createRawContact(); 578 insertStructuredName(rawContactId3, "Dwayne", null); 579 580 // Edit distance == 0.6 581 long rawContactId4 = createRawContact(); 582 insertStructuredName(rawContactId4, "Donny", null); 583 584 long contactId1 = queryContactId(rawContactId1); 585 long contactId2 = queryContactId(rawContactId2); 586 long contactId3 = queryContactId(rawContactId3); 587 588 assertSuggestions(contactId1, contactId2, contactId3); 589 } 590 591 public void testAggregationSuggestionsBasedOnPhoneNumber() { 592 593 // Create two contacts that would not be aggregated because of name mismatch 594 long rawContactId1 = createRawContact(); 595 insertStructuredName(rawContactId1, "Lord", "Farquaad"); 596 insertPhoneNumber(rawContactId1, "(888)555-1236"); 597 598 long rawContactId2 = createRawContact(); 599 insertStructuredName(rawContactId2, "Talking", "Donkey"); 600 insertPhoneNumber(rawContactId2, "1(888)555-1236"); 601 602 long contactId1 = queryContactId(rawContactId1); 603 long contactId2 = queryContactId(rawContactId2); 604 assertTrue(contactId1 != contactId2); 605 606 assertSuggestions(contactId1, contactId2); 607 } 608 609 public void testAggregationSuggestionsBasedOnEmailAddress() { 610 611 // Create two contacts that would not be aggregated because of name mismatch 612 long rawContactId1 = createRawContact(); 613 insertStructuredName(rawContactId1, "Carl", "Fredricksen"); 614 insertEmail(rawContactId1, "up (at) android.com"); 615 616 long rawContactId2 = createRawContact(); 617 insertStructuredName(rawContactId2, "Charles", "Muntz"); 618 insertEmail(rawContactId2, "up (at) android.com"); 619 620 long contactId1 = queryContactId(rawContactId1); 621 long contactId2 = queryContactId(rawContactId2); 622 assertTrue(contactId1 != contactId2); 623 624 assertSuggestions(contactId1, contactId2); 625 } 626 627 public void testAggregationSuggestionsBasedOnEmailAddressApproximateMatch() { 628 629 // Create two contacts that would not be aggregated because of name mismatch 630 long rawContactId1 = createRawContact(); 631 insertStructuredName(rawContactId1, "Bob", null); 632 insertEmail(rawContactId1, "incredible (at) android.com"); 633 634 long rawContactId2 = createRawContact(); 635 insertStructuredName(rawContactId2, "Lucius", "Best"); 636 insertEmail(rawContactId2, "incrediball (at) android.com"); 637 638 long contactId1 = queryContactId(rawContactId1); 639 long contactId2 = queryContactId(rawContactId2); 640 assertTrue(contactId1 != contactId2); 641 642 assertSuggestions(contactId1, contactId2); 643 } 644 645 public void testAggregationSuggestionsBasedOnNickname() { 646 long rawContactId1 = createRawContact(); 647 insertStructuredName(rawContactId1, "Peter", "Parker"); 648 insertNickname(rawContactId1, "Spider-Man"); 649 650 long rawContactId2 = createRawContact(); 651 insertStructuredName(rawContactId2, "Manny", "Spider"); 652 653 long contactId1 = queryContactId(rawContactId1); 654 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 655 rawContactId1, rawContactId2); 656 657 long contactId2 = queryContactId(rawContactId2); 658 assertSuggestions(contactId1, contactId2); 659 } 660 661 public void testAggregationSuggestionsBasedOnNicknameMatchingName() { 662 long rawContactId1 = createRawContact(); 663 insertStructuredName(rawContactId1, "Clark", "Kent"); 664 insertNickname(rawContactId1, "Superman"); 665 666 long rawContactId2 = createRawContact(); 667 insertStructuredName(rawContactId2, "Roy", "Williams"); 668 insertNickname(rawContactId2, "superman"); 669 670 long contactId1 = queryContactId(rawContactId1); 671 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 672 rawContactId1, rawContactId2); 673 674 long contactId2 = queryContactId(rawContactId2); 675 assertSuggestions(contactId1, contactId2); 676 } 677 678 public void testAggregationSuggestionsBasedOnCommonNickname() { 679 long rawContactId1 = createRawContact(); 680 insertStructuredName(rawContactId1, "Dick", "Cherry"); 681 682 long rawContactId2 = createRawContact(); 683 insertStructuredName(rawContactId2, "Richard", "Cherry"); 684 685 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 686 rawContactId1, rawContactId2); 687 688 long contactId1 = queryContactId(rawContactId1); 689 long contactId2 = queryContactId(rawContactId2); 690 assertSuggestions(contactId1, contactId2); 691 } 692 693 public void testAggregationSuggestionsBasedOnPhoneNumberWithFilter() { 694 695 // Create two contacts that would not be aggregated because of name mismatch 696 long rawContactId1 = createRawContact(); 697 insertStructuredName(rawContactId1, "Lord", "Farquaad"); 698 insertPhoneNumber(rawContactId1, "(888)555-1236"); 699 700 long rawContactId2 = createRawContact(); 701 insertStructuredName(rawContactId2, "Talking", "Donkey"); 702 insertPhoneNumber(rawContactId2, "1(888)555-1236"); 703 704 long contactId1 = queryContactId(rawContactId1); 705 long contactId2 = queryContactId(rawContactId2); 706 assertTrue(contactId1 != contactId2); 707 708 assertSuggestions(contactId1, "talk", contactId2); 709 assertSuggestions(contactId1, "don", contactId2); 710 assertSuggestions(contactId1, "", contactId2); 711 assertSuggestions(contactId1, "eddie"); 712 } 713 714 public void testChoosePhotoSetBeforeAggregation() { 715 long rawContactId1 = createRawContact(); 716 setContactAccount(rawContactId1, "donut", "donut_act"); 717 insertPhoto(rawContactId1); 718 719 long rawContactId2 = createRawContact(); 720 setContactAccount(rawContactId2, "cupcake", "cupcake_act"); 721 long cupcakeId = ContentUris.parseId(insertPhoto(rawContactId2)); 722 723 long rawContactId3 = createRawContact(); 724 setContactAccount(rawContactId3, "froyo", "froyo_act"); 725 insertPhoto(rawContactId3); 726 727 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 728 rawContactId1, rawContactId2); 729 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 730 rawContactId1, rawContactId3); 731 assertEquals(cupcakeId, queryPhotoId(queryContactId(rawContactId2))); 732 } 733 734 public void testChoosePhotoSetAfterAggregation() { 735 long rawContactId1 = createRawContact(); 736 setContactAccount(rawContactId1, "donut", "donut_act"); 737 insertPhoto(rawContactId1); 738 739 long rawContactId2 = createRawContact(); 740 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 741 rawContactId1, rawContactId2); 742 setContactAccount(rawContactId2, "cupcake", "cupcake_act"); 743 long cupcakeId = ContentUris.parseId(insertPhoto(rawContactId2)); 744 745 long rawContactId3 = createRawContact(); 746 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 747 rawContactId1, rawContactId3); 748 setContactAccount(rawContactId3, "froyo", "froyo_act"); 749 insertPhoto(rawContactId3); 750 751 assertEquals(cupcakeId, queryPhotoId(queryContactId(rawContactId2))); 752 } 753 754 public void testDisplayNameSources() { 755 long rawContactId = createRawContact(); 756 long contactId = queryContactId(rawContactId); 757 758 assertNull(queryDisplayName(contactId)); 759 760 insertEmail(rawContactId, "eclair (at) android.com"); 761 assertEquals("eclair (at) android.com", queryDisplayName(contactId)); 762 763 insertPhoneNumber(rawContactId, "800-555-5555"); 764 assertEquals("800-555-5555", queryDisplayName(contactId)); 765 766 ContentValues values = new ContentValues(); 767 values.put(Organization.COMPANY, "Android"); 768 insertOrganization(rawContactId, values); 769 assertEquals("Android", queryDisplayName(contactId)); 770 771 insertNickname(rawContactId, "Dro"); 772 assertEquals("Dro", queryDisplayName(contactId)); 773 774 values.clear(); 775 values.put(StructuredName.GIVEN_NAME, "Eclair"); 776 values.put(StructuredName.FAMILY_NAME, "Android"); 777 insertStructuredName(rawContactId, values); 778 assertEquals("Eclair Android", queryDisplayName(contactId)); 779 } 780 781 public void testVerifiedName() { 782 long rawContactId1 = createRawContactWithName("test1", "TEST1", ACCOUNT_1); 783 storeValue(RawContacts.CONTENT_URI, rawContactId1, RawContacts.NAME_VERIFIED, "1"); 784 long rawContactId2 = createRawContactWithName("test2", "TEST2", ACCOUNT_2); 785 long rawContactId3 = createRawContactWithName("test3", "TEST3 LONG", ACCOUNT_3); 786 787 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, 788 rawContactId2); 789 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, 790 rawContactId3); 791 792 long contactId = queryContactId(rawContactId1); 793 794 // Should be the verified name 795 assertEquals("test1 TEST1", queryDisplayName(contactId)); 796 797 // Mark a different name as verified - this should reset the NAME_VERIFIED field 798 // for the other rawContacts 799 storeValue(RawContacts.CONTENT_URI, rawContactId2, RawContacts.NAME_VERIFIED, "1"); 800 assertStoredValue(RawContacts.CONTENT_URI, rawContactId1, RawContacts.NAME_VERIFIED, 0); 801 assertEquals("test2 TEST2", queryDisplayName(contactId)); 802 803 // Reset the NAME_VERIFIED flag - now the most complex of the three names should win 804 storeValue(RawContacts.CONTENT_URI, rawContactId2, RawContacts.NAME_VERIFIED, "0"); 805 assertEquals("test3 TEST3 LONG", queryDisplayName(contactId)); 806 } 807 808 public void testAggregationModeSuspendedSeparateTransactions() { 809 810 // Setting aggregation mode to SUSPENDED should prevent aggregation from happening 811 long rawContactId1 = createRawContact(ACCOUNT_1); 812 storeValue(RawContacts.CONTENT_URI, rawContactId1, 813 RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED); 814 Uri name1 = insertStructuredName(rawContactId1, "THE", "SAME"); 815 816 long rawContactId2 = createRawContact(ACCOUNT_2); 817 storeValue(RawContacts.CONTENT_URI, rawContactId2, 818 RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED); 819 insertStructuredName(rawContactId2, "THE", "SAME"); 820 821 assertNotAggregated(rawContactId1, rawContactId2); 822 823 // Changing aggregation mode to DEFAULT should change nothing 824 storeValue(RawContacts.CONTENT_URI, rawContactId1, 825 RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT); 826 storeValue(RawContacts.CONTENT_URI, rawContactId2, 827 RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT); 828 assertNotAggregated(rawContactId1, rawContactId2); 829 830 // Changing the name should trigger aggregation 831 storeValue(name1, StructuredName.GIVEN_NAME, "the"); 832 assertAggregated(rawContactId1, rawContactId2); 833 } 834 835 public void testAggregationModeInitializedAsSuspended() throws Exception { 836 837 // Setting aggregation mode to SUSPENDED should prevent aggregation from happening 838 ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 839 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 840 .build(); 841 ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 842 .withValueBackReference(Data.RAW_CONTACT_ID, 0) 843 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 844 .withValue(StructuredName.GIVEN_NAME, "John") 845 .withValue(StructuredName.FAMILY_NAME, "Doe") 846 .build(); 847 ContentProviderOperation cpo3 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 848 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 849 .build(); 850 ContentProviderOperation cpo4 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 851 .withValueBackReference(Data.RAW_CONTACT_ID, 2) 852 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 853 .withValue(StructuredName.GIVEN_NAME, "John") 854 .withValue(StructuredName.FAMILY_NAME, "Doe") 855 .build(); 856 ContentProviderOperation cpo5 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 857 .withSelection(RawContacts._ID + "=?", new String[1]) 858 .withSelectionBackReference(0, 0) 859 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT) 860 .build(); 861 ContentProviderOperation cpo6 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 862 .withSelection(RawContacts._ID + "=?", new String[1]) 863 .withSelectionBackReference(0, 2) 864 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT) 865 .build(); 866 867 ContentProviderResult[] results = 868 mResolver.applyBatch(ContactsContract.AUTHORITY, 869 Lists.newArrayList(cpo1, cpo2, cpo3, cpo4, cpo5, cpo6)); 870 871 long rawContactId1 = ContentUris.parseId(results[0].uri); 872 long rawContactId2 = ContentUris.parseId(results[2].uri); 873 874 assertNotAggregated(rawContactId1, rawContactId2); 875 } 876 877 public void testAggregationModeUpdatedToSuspended() throws Exception { 878 879 // Setting aggregation mode to SUSPENDED should prevent aggregation from happening 880 ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 881 .withValues(new ContentValues()) 882 .build(); 883 ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 884 .withValueBackReference(Data.RAW_CONTACT_ID, 0) 885 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 886 .withValue(StructuredName.GIVEN_NAME, "John") 887 .withValue(StructuredName.FAMILY_NAME, "Doe") 888 .build(); 889 ContentProviderOperation cpo3 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 890 .withValues(new ContentValues()) 891 .build(); 892 ContentProviderOperation cpo4 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 893 .withValueBackReference(Data.RAW_CONTACT_ID, 2) 894 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 895 .withValue(StructuredName.GIVEN_NAME, "John") 896 .withValue(StructuredName.FAMILY_NAME, "Doe") 897 .build(); 898 ContentProviderOperation cpo5 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 899 .withSelection(RawContacts._ID + "=?", new String[1]) 900 .withSelectionBackReference(0, 0) 901 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 902 .build(); 903 ContentProviderOperation cpo6 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 904 .withSelection(RawContacts._ID + "=?", new String[1]) 905 .withSelectionBackReference(0, 2) 906 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 907 .build(); 908 909 ContentProviderResult[] results = 910 mResolver.applyBatch(ContactsContract.AUTHORITY, 911 Lists.newArrayList(cpo1, cpo2, cpo3, cpo4, cpo5, cpo6)); 912 913 long rawContactId1 = ContentUris.parseId(results[0].uri); 914 long rawContactId2 = ContentUris.parseId(results[2].uri); 915 916 assertNotAggregated(rawContactId1, rawContactId2); 917 } 918 919 public void testAggregationModeSuspendedOverriddenByAggException() throws Exception { 920 ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 921 .withValue(RawContacts.ACCOUNT_NAME, "a") 922 .withValue(RawContacts.ACCOUNT_TYPE, "b") 923 .build(); 924 ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 925 .withValueBackReference(Data.RAW_CONTACT_ID, 0) 926 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 927 .withValue(StructuredName.GIVEN_NAME, "John") 928 .withValue(StructuredName.FAMILY_NAME, "Doe") 929 .build(); 930 ContentProviderOperation cpo3 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 931 .withValue(RawContacts.ACCOUNT_NAME, "c") 932 .withValue(RawContacts.ACCOUNT_TYPE, "d") 933 .build(); 934 ContentProviderOperation cpo4 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 935 .withValueBackReference(Data.RAW_CONTACT_ID, 2) 936 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 937 .withValue(StructuredName.GIVEN_NAME, "John") 938 .withValue(StructuredName.FAMILY_NAME, "Doe") 939 .build(); 940 ContentProviderOperation cpo5 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 941 .withSelection(RawContacts._ID + "=?", new String[1]) 942 .withSelectionBackReference(0, 0) 943 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 944 .build(); 945 ContentProviderOperation cpo6 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 946 .withSelection(RawContacts._ID + "=?", new String[1]) 947 .withSelectionBackReference(0, 2) 948 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 949 .build(); 950 951 // Checking that aggregation mode SUSPENDED should be overridden by inserting 952 // an explicit aggregation exception 953 ContentProviderOperation cpo7 = 954 ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI) 955 .withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1, 0) 956 .withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, 2) 957 .withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER) 958 .build(); 959 960 ContentProviderResult[] results = 961 mResolver.applyBatch(ContactsContract.AUTHORITY, 962 Lists.newArrayList(cpo1, cpo2, cpo3, cpo4, cpo5, cpo6, cpo7)); 963 964 long rawContactId1 = ContentUris.parseId(results[0].uri); 965 long rawContactId2 = ContentUris.parseId(results[2].uri); 966 967 assertAggregated(rawContactId1, rawContactId2); 968 } 969 970 private void assertSuggestions(long contactId, long... suggestions) { 971 final Uri aggregateUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 972 Uri uri = Uri.withAppendedPath(aggregateUri, 973 Contacts.AggregationSuggestions.CONTENT_DIRECTORY); 974 assertSuggestions(uri, suggestions); 975 } 976 977 private void assertSuggestions(long contactId, String filter, long... suggestions) { 978 final Uri aggregateUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 979 Uri uri = Uri.withAppendedPath(Uri.withAppendedPath(aggregateUri, 980 Contacts.AggregationSuggestions.CONTENT_DIRECTORY), Uri.encode(filter)); 981 assertSuggestions(uri, suggestions); 982 } 983 984 private void assertSuggestions(Uri uri, long... suggestions) { 985 final Cursor cursor = mResolver.query(uri, 986 new String[] { Contacts._ID, Contacts.CONTACT_PRESENCE }, 987 null, null, null); 988 989 assertEquals(suggestions.length, cursor.getCount()); 990 991 for (int i = 0; i < suggestions.length; i++) { 992 cursor.moveToNext(); 993 assertEquals(suggestions[i], cursor.getLong(0)); 994 } 995 996 cursor.close(); 997 } 998 } 999