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.aggregation; 18 19 import android.accounts.Account; 20 import android.content.ContentProviderOperation; 21 import android.content.ContentProviderResult; 22 import android.content.ContentUris; 23 import android.content.ContentValues; 24 import android.database.Cursor; 25 import android.net.Uri; 26 import android.provider.ContactsContract; 27 import android.provider.ContactsContract.AggregationExceptions; 28 import android.provider.ContactsContract.CommonDataKinds.Organization; 29 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 30 import android.provider.ContactsContract.Contacts; 31 import android.provider.ContactsContract.Contacts.AggregationSuggestions; 32 import android.provider.ContactsContract.Contacts.Photo; 33 import android.provider.ContactsContract.Data; 34 import android.provider.ContactsContract.RawContacts; 35 import android.provider.ContactsContract.StatusUpdates; 36 import android.test.MoreAsserts; 37 import android.test.suitebuilder.annotation.MediumTest; 38 39 import com.android.providers.contacts.BaseContactsProvider2Test; 40 import com.android.providers.contacts.TestUtils; 41 import com.android.providers.contacts.tests.R; 42 import com.google.android.collect.Lists; 43 44 /** 45 * Unit tests for {@link ContactAggregator}. 46 * 47 * Run the test like this: 48 * <code> 49 * adb shell am instrument -e class com.android.providers.contacts.ContactAggregatorTest -w \ 50 * com.android.providers.contacts.tests/android.test.InstrumentationTestRunner 51 * </code> 52 */ 53 @MediumTest 54 public class ContactAggregatorTest extends BaseContactsProvider2Test { 55 56 private static final Account ACCOUNT_1 = new Account("account_name_1", "account_type_1"); 57 private static final Account ACCOUNT_2 = new Account("account_name_2", "account_type_2"); 58 private static final Account ACCOUNT_3 = new Account("account_name_3", "account_type_3"); 59 60 private static final String[] AGGREGATION_EXCEPTION_PROJECTION = new String[] { 61 AggregationExceptions.TYPE, 62 AggregationExceptions.RAW_CONTACT_ID1, 63 AggregationExceptions.RAW_CONTACT_ID2 64 }; 65 66 public void testCrudAggregationExceptions() throws Exception { 67 long rawContactId1 = createRawContactWithName("zz", "top"); 68 long rawContactId2 = createRawContactWithName("aa", "bottom"); 69 70 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 71 rawContactId1, rawContactId2); 72 73 String selection = "(" + AggregationExceptions.RAW_CONTACT_ID1 + "=" + rawContactId1 74 + " AND " + AggregationExceptions.RAW_CONTACT_ID2 + "=" + rawContactId2 75 + ") OR (" + AggregationExceptions.RAW_CONTACT_ID1 + "=" + rawContactId2 76 + " AND " + AggregationExceptions.RAW_CONTACT_ID2 + "=" + rawContactId1 + ")"; 77 78 // Refetch the row we have just inserted 79 Cursor c = mResolver.query(AggregationExceptions.CONTENT_URI, 80 AGGREGATION_EXCEPTION_PROJECTION, selection, null, null); 81 82 assertTrue(c.moveToFirst()); 83 assertEquals(AggregationExceptions.TYPE_KEEP_TOGETHER, c.getInt(0)); 84 assertTrue((rawContactId1 == c.getLong(1) && rawContactId2 == c.getLong(2)) 85 || (rawContactId2 == c.getLong(1) && rawContactId1 == c.getLong(2))); 86 assertFalse(c.moveToNext()); 87 c.close(); 88 89 // Change from TYPE_KEEP_IN to TYPE_KEEP_OUT 90 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 91 rawContactId1, rawContactId2); 92 93 c = mResolver.query(AggregationExceptions.CONTENT_URI, AGGREGATION_EXCEPTION_PROJECTION, 94 selection, null, null); 95 96 assertTrue(c.moveToFirst()); 97 assertEquals(AggregationExceptions.TYPE_KEEP_SEPARATE, c.getInt(0)); 98 assertTrue((rawContactId1 == c.getLong(1) && rawContactId2 == c.getLong(2)) 99 || (rawContactId2 == c.getLong(1) && rawContactId1 == c.getLong(2))); 100 assertFalse(c.moveToNext()); 101 c.close(); 102 103 // Delete the rule 104 setAggregationException(AggregationExceptions.TYPE_AUTOMATIC, 105 rawContactId1, rawContactId2); 106 107 // Verify that the row is gone 108 c = mResolver.query(AggregationExceptions.CONTENT_URI, AGGREGATION_EXCEPTION_PROJECTION, 109 selection, null, null); 110 assertFalse(c.moveToFirst()); 111 c.close(); 112 } 113 114 public void testAggregationCreatesNewAggregate() { 115 long rawContactId = createRawContact(); 116 117 Uri resultUri = insertStructuredName(rawContactId, "Johna", "Smitha"); 118 119 // Parse the URI and confirm that it contains an ID 120 assertTrue(ContentUris.parseId(resultUri) != 0); 121 122 long contactId = queryContactId(rawContactId); 123 assertTrue(contactId != 0); 124 125 String displayName = queryDisplayName(contactId); 126 assertEquals("Johna Smitha", displayName); 127 } 128 129 public void testAggregationOfExactFullNameMatch() { 130 long rawContactId1 = createRawContact(ACCOUNT_1); 131 insertStructuredName(rawContactId1, "Johnb", "Smithb"); 132 133 long rawContactId2 = createRawContact(ACCOUNT_2); 134 insertStructuredName(rawContactId2, "Johnb", "Smithb"); 135 136 assertAggregated(rawContactId1, rawContactId2, "Johnb Smithb"); 137 } 138 139 public void testAggregationIgnoresInvisibleContact() { 140 Account account = new Account("accountName", "accountType"); 141 createAutoAddGroup(account); 142 143 long rawContactId1 = createRawContact(account); 144 insertStructuredName(rawContactId1, "Flynn", "Ryder"); 145 146 // Hide by removing from all groups 147 removeGroupMemberships(rawContactId1); 148 149 long rawContactId2 = createRawContact(account); 150 insertStructuredName(rawContactId2, "Flynn", "Ryder"); 151 152 long rawContactId3 = createRawContact(ACCOUNT_2); 153 insertStructuredName(rawContactId3, "Flynn", "Ryder"); 154 155 assertNotAggregated(rawContactId1, rawContactId2); 156 assertNotAggregated(rawContactId1, rawContactId3); 157 assertAggregated(rawContactId2, rawContactId3, "Flynn Ryder"); 158 } 159 160 public void testAggregationOfCaseInsensitiveFullNameMatch() { 161 long rawContactId1 = createRawContact(ACCOUNT_1); 162 insertStructuredName(rawContactId1, "Johnc", "Smithc"); 163 164 long rawContactId2 = createRawContact(ACCOUNT_2); 165 insertStructuredName(rawContactId2, "Johnc", "smithc"); 166 167 assertAggregated(rawContactId1, rawContactId2, "Johnc Smithc"); 168 } 169 170 public void testAggregationOfLastNameMatch() { 171 long rawContactId1 = createRawContact(ACCOUNT_1); 172 insertStructuredName(rawContactId1, null, "Johnd"); 173 174 long rawContactId2 = createRawContact(ACCOUNT_2); 175 insertStructuredName(rawContactId2, null, "johnd"); 176 177 assertAggregated(rawContactId1, rawContactId2, "Johnd"); 178 } 179 180 public void testNonAggregationOfFirstNameMatch() { 181 long rawContactId1 = createRawContact(ACCOUNT_1); 182 insertStructuredName(rawContactId1, "Johne", "Smithe"); 183 184 long rawContactId2 = createRawContact(ACCOUNT_2); 185 insertStructuredName(rawContactId2, "Johne", null); 186 187 assertNotAggregated(rawContactId1, rawContactId2); 188 } 189 190 public void testNonAggregationOfLastNameMatch() { 191 long rawContactId1 = createRawContact(ACCOUNT_1); 192 insertStructuredName(rawContactId1, "Johnf", "Smithf"); 193 194 long rawContactId2 = createRawContact(ACCOUNT_2); 195 insertStructuredName(rawContactId2, null, "Smithf"); 196 197 assertNotAggregated(rawContactId1, rawContactId2); 198 } 199 200 public void testAggregationOfConcatenatedFullNameMatch() { 201 long rawContactId1 = createRawContact(ACCOUNT_1); 202 insertStructuredName(rawContactId1, "Johng", "Smithg"); 203 204 long rawContactId2 = createRawContact(ACCOUNT_2); 205 insertStructuredName(rawContactId2, "johngsmithg", null); 206 207 assertAggregated(rawContactId1, rawContactId2, "Johng Smithg"); 208 } 209 210 public void testAggregationOfNormalizedFullNameMatch() { 211 long rawContactId1 = createRawContact(ACCOUNT_1); 212 insertStructuredName(rawContactId1, "H\u00e9l\u00e8ne", "Bj\u00f8rn"); 213 214 long rawContactId2 = createRawContact(ACCOUNT_2); 215 insertStructuredName(rawContactId2, "helene bjorn", null); 216 217 assertAggregated(rawContactId1, rawContactId2, "H\u00e9l\u00e8ne Bj\u00f8rn"); 218 } 219 220 public void testAggregationOfNormalizedFullNameMatchWithReadOnlyAccount() { 221 long rawContactId1 = createRawContact(new Account("acct", READ_ONLY_ACCOUNT_TYPE)); 222 insertStructuredName(rawContactId1, "H\u00e9l\u00e8ne", "Bj\u00f8rn"); 223 224 long rawContactId2 = createRawContact(); 225 insertStructuredName(rawContactId2, "helene bjorn", null); 226 227 assertAggregated(rawContactId1, rawContactId2, "helene bjorn"); 228 } 229 230 public void testAggregationOfNumericNames() { 231 long rawContactId1 = createRawContact(ACCOUNT_1); 232 insertStructuredName(rawContactId1, "123", null); 233 234 long rawContactId2 = createRawContact(ACCOUNT_2); 235 insertStructuredName(rawContactId2, "1-2-3", null); 236 237 assertAggregated(rawContactId1, rawContactId2, "1-2-3"); 238 } 239 240 public void testAggregationOfInconsistentlyParsedNames() { 241 long rawContactId1 = createRawContact(ACCOUNT_1); 242 243 ContentValues values = new ContentValues(); 244 values.put(StructuredName.DISPLAY_NAME, "604 Arizona Ave"); 245 values.put(StructuredName.GIVEN_NAME, "604"); 246 values.put(StructuredName.MIDDLE_NAME, "Arizona"); 247 values.put(StructuredName.FAMILY_NAME, "Ave"); 248 insertStructuredName(rawContactId1, values); 249 250 long rawContactId2 = createRawContact(ACCOUNT_2); 251 values.clear(); 252 values.put(StructuredName.DISPLAY_NAME, "604 Arizona Ave"); 253 values.put(StructuredName.GIVEN_NAME, "604"); 254 values.put(StructuredName.FAMILY_NAME, "Arizona Ave"); 255 insertStructuredName(rawContactId2, values); 256 257 assertAggregated(rawContactId1, rawContactId2, "604 Arizona Ave"); 258 } 259 260 public void testAggregationBasedOnMiddleName() { 261 ContentValues values = new ContentValues(); 262 long rawContactId1 = createRawContact(ACCOUNT_1); 263 values.put(StructuredName.GIVEN_NAME, "John"); 264 values.put(StructuredName.GIVEN_NAME, "Abigale"); 265 values.put(StructuredName.FAMILY_NAME, "James"); 266 267 insertStructuredName(rawContactId1, values); 268 269 long rawContactId2 = createRawContact(ACCOUNT_2); 270 values.clear(); 271 values.put(StructuredName.GIVEN_NAME, "John"); 272 values.put(StructuredName.GIVEN_NAME, "Marie"); 273 values.put(StructuredName.FAMILY_NAME, "James"); 274 insertStructuredName(rawContactId2, values); 275 276 assertNotAggregated(rawContactId1, rawContactId2); 277 } 278 279 public void testAggregationBasedOnPhoneNumberNoNameData() { 280 long rawContactId1 = createRawContact(ACCOUNT_1); 281 insertPhoneNumber(rawContactId1, "(888)555-1231"); 282 283 long rawContactId2 = createRawContact(ACCOUNT_2); 284 insertPhoneNumber(rawContactId2, "1(888)555-1231"); 285 286 assertAggregated(rawContactId1, rawContactId2); 287 } 288 289 public void testAggregationBasedOnPhoneNumberWhenTargetAggregateHasNoName() { 290 long rawContactId1 = createRawContact(ACCOUNT_1); 291 insertPhoneNumber(rawContactId1, "(888)555-1232"); 292 293 long rawContactId2 = createRawContact(ACCOUNT_2); 294 insertStructuredName(rawContactId2, "Johnl", "Smithl"); 295 insertPhoneNumber(rawContactId2, "1(888)555-1232"); 296 297 assertAggregated(rawContactId1, rawContactId2); 298 } 299 300 public void testAggregationBasedOnPhoneNumberWhenNewContactHasNoName() { 301 long rawContactId1 = createRawContact(ACCOUNT_1); 302 insertStructuredName(rawContactId1, "Johnm", "Smithm"); 303 insertPhoneNumber(rawContactId1, "(888)555-1233"); 304 305 long rawContactId2 = createRawContact(ACCOUNT_2); 306 insertPhoneNumber(rawContactId2, "1(888)555-1233"); 307 308 assertAggregated(rawContactId1, rawContactId2); 309 } 310 311 public void testAggregationBasedOnPhoneNumberWithDifferentNames() { 312 long rawContactId1 = createRawContact(ACCOUNT_1); 313 insertStructuredName(rawContactId1, "Baby", "Bear"); 314 insertPhoneNumber(rawContactId1, "(888)555-1235"); 315 316 long rawContactId2 = createRawContact(ACCOUNT_2); 317 insertStructuredName(rawContactId2, "Blind", "Mouse"); 318 insertPhoneNumber(rawContactId2, "1(888)555-1235"); 319 320 assertNotAggregated(rawContactId1, rawContactId2); 321 } 322 323 public void testAggregationBasedOnPhoneNumberWithJustFirstName() { 324 long rawContactId1 = createRawContact(ACCOUNT_1); 325 insertStructuredName(rawContactId1, "Chick", "Notnull"); 326 insertPhoneNumber(rawContactId1, "(888)555-1236"); 327 328 long rawContactId2 = createRawContact(ACCOUNT_2); 329 insertStructuredName(rawContactId2, "Chick", null); 330 insertPhoneNumber(rawContactId2, "1(888)555-1236"); 331 332 assertAggregated(rawContactId1, rawContactId2); 333 } 334 335 public void testAggregationBasedOnEmailNoNameData() { 336 long rawContactId1 = createRawContact(ACCOUNT_1); 337 insertEmail(rawContactId1, "lightning (at) android.com"); 338 339 long rawContactId2 = createRawContact(ACCOUNT_2); 340 insertEmail(rawContactId2, "lightning (at) android.com"); 341 342 assertAggregated(rawContactId1, rawContactId2); 343 } 344 345 public void testAggregationBasedOnEmailWhenTargetAggregateHasNoName() { 346 long rawContactId1 = createRawContact(ACCOUNT_1); 347 insertEmail(rawContactId1, "mcqueen (at) android.com"); 348 349 long rawContactId2 = createRawContact(ACCOUNT_2); 350 insertStructuredName(rawContactId2, "Lightning", "McQueen"); 351 insertEmail(rawContactId2, "mcqueen (at) android.com"); 352 353 assertAggregated(rawContactId1, rawContactId2, "Lightning McQueen"); 354 } 355 356 public void testAggregationBasedOnEmailWhenNewContactHasNoName() { 357 long rawContactId1 = createRawContact(ACCOUNT_1); 358 insertStructuredName(rawContactId1, "Doc", "Hudson"); 359 insertEmail(rawContactId1, "doc (at) android.com"); 360 361 long rawContactId2 = createRawContact(ACCOUNT_2); 362 insertEmail(rawContactId2, "doc (at) android.com"); 363 364 assertAggregated(rawContactId1, rawContactId2); 365 } 366 367 public void testAggregationBasedOnEmailWithDifferentNames() { 368 long rawContactId1 = createRawContact(ACCOUNT_1); 369 insertStructuredName(rawContactId1, "Chick", "Hicks"); 370 insertEmail(rawContactId1, "hicky (at) android.com"); 371 372 long rawContactId2 = createRawContact(ACCOUNT_2); 373 insertStructuredName(rawContactId2, "Luigi", "Guido"); 374 insertEmail(rawContactId2, "hicky (at) android.com"); 375 376 assertNotAggregated(rawContactId1, rawContactId2); 377 } 378 379 public void testAggregationByCommonNicknameWithLastName() { 380 long rawContactId1 = createRawContact(ACCOUNT_1); 381 insertStructuredName(rawContactId1, "Bill", "Gore"); 382 383 long rawContactId2 = createRawContact(ACCOUNT_2); 384 insertStructuredName(rawContactId2, "William", "Gore"); 385 386 assertAggregated(rawContactId1, rawContactId2, "William Gore"); 387 } 388 389 public void testAggregationByCommonNicknameOnly() { 390 long rawContactId1 = createRawContact(ACCOUNT_1); 391 insertStructuredName(rawContactId1, "Lawrence", null); 392 393 long rawContactId2 = createRawContact(ACCOUNT_2); 394 insertStructuredName(rawContactId2, "Larry", null); 395 396 assertAggregated(rawContactId1, rawContactId2, "Lawrence"); 397 } 398 399 public void testAggregationByNicknameNoStructuredName() { 400 long rawContactId1 = createRawContact(ACCOUNT_1); 401 insertNickname(rawContactId1, "Frozone"); 402 403 long rawContactId2 = createRawContact(ACCOUNT_2); 404 insertNickname(rawContactId2, "Frozone"); 405 406 assertAggregated(rawContactId1, rawContactId2); 407 } 408 409 public void testAggregationByNicknameWithDifferentNames() { 410 long rawContactId1 = createRawContact(ACCOUNT_1); 411 insertStructuredName(rawContactId1, "Helen", "Parr"); 412 insertNickname(rawContactId1, "Elastigirl"); 413 414 long rawContactId2 = createRawContact(ACCOUNT_2); 415 insertStructuredName(rawContactId2, "Shawn", "Johnson"); 416 insertNickname(rawContactId2, "Elastigirl"); 417 418 assertNotAggregated(rawContactId1, rawContactId2); 419 } 420 421 public void testNonAggregationOnOrganization() { 422 ContentValues values = new ContentValues(); 423 values.put(Organization.TITLE, "Monsters, Inc"); 424 long rawContactId1 = createRawContact(ACCOUNT_1); 425 insertOrganization(rawContactId1, values); 426 insertNickname(rawContactId1, "Boo"); 427 428 long rawContactId2 = createRawContact(ACCOUNT_2); 429 insertOrganization(rawContactId2, values); 430 insertNickname(rawContactId2, "Rendall"); // To force reaggregation 431 432 assertNotAggregated(rawContactId1, rawContactId2); 433 } 434 435 public void testAggregationByIdentity() { 436 long rawContactId1 = createRawContact(ACCOUNT_1); 437 insertIdentity(rawContactId1, "iden1", "namespace1"); 438 439 long rawContactId2 = createRawContact(ACCOUNT_2); 440 insertIdentity(rawContactId2, "iden1", "namespace1"); 441 442 assertAggregated(rawContactId1, rawContactId2); 443 } 444 445 public void testAggregationExceptionKeepIn() { 446 long rawContactId1 = createRawContact(ACCOUNT_1); 447 insertStructuredName(rawContactId1, "Johnk", "Smithk"); 448 449 long rawContactId2 = createRawContact(ACCOUNT_2); 450 insertStructuredName(rawContactId2, "Johnkx", "Smithkx"); 451 452 long contactId1 = queryContactId(rawContactId1); 453 long contactId2 = queryContactId(rawContactId2); 454 455 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 456 rawContactId1, rawContactId2); 457 458 assertAggregated(rawContactId1, rawContactId2, "Johnkx Smithkx"); 459 460 // Assert that the empty aggregate got removed 461 long newContactId1 = queryContactId(rawContactId1); 462 if (contactId1 != newContactId1) { 463 Cursor cursor = queryContact(contactId1); 464 assertFalse(cursor.moveToFirst()); 465 cursor.close(); 466 } else { 467 Cursor cursor = queryContact(contactId2); 468 assertFalse(cursor.moveToFirst()); 469 cursor.close(); 470 } 471 } 472 473 public void testAggregationExceptionKeepOut() { 474 long rawContactId1 = createRawContact(ACCOUNT_1); 475 insertStructuredName(rawContactId1, "Johnh", "Smithh"); 476 477 long rawContactId2 = createRawContact(ACCOUNT_2); 478 insertStructuredName(rawContactId2, "Johnh", "Smithh"); 479 480 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 481 rawContactId1, rawContactId2); 482 483 assertNotAggregated(rawContactId1, rawContactId2); 484 } 485 486 public void testAggregationExceptionKeepOutCheckUpdatesDisplayName() { 487 long rawContactId1 = createRawContact(ACCOUNT_1); 488 insertStructuredName(rawContactId1, "Johni", "Smithi"); 489 490 long rawContactId2 = createRawContact(ACCOUNT_2); 491 insertStructuredName(rawContactId2, "Johnj", "Smithj"); 492 493 long rawContactId3 = createRawContact(ACCOUNT_3); 494 insertStructuredName(rawContactId3, "Johnm", "Smithm"); 495 496 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 497 rawContactId1, rawContactId2); 498 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 499 rawContactId1, rawContactId3); 500 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 501 rawContactId2, rawContactId3); 502 503 assertAggregated(rawContactId1, rawContactId2, "Johnm Smithm"); 504 assertAggregated(rawContactId1, rawContactId3); 505 506 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 507 rawContactId1, rawContactId2); 508 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 509 rawContactId1, rawContactId3); 510 511 assertNotAggregated(rawContactId1, rawContactId2); 512 assertNotAggregated(rawContactId1, rawContactId3); 513 514 String displayName1 = queryDisplayName(queryContactId(rawContactId1)); 515 assertEquals("Johni Smithi", displayName1); 516 517 assertAggregated(rawContactId2, rawContactId3, "Johnm Smithm"); 518 519 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 520 rawContactId2, rawContactId3); 521 assertNotAggregated(rawContactId1, rawContactId2); 522 assertNotAggregated(rawContactId1, rawContactId3); 523 assertNotAggregated(rawContactId2, rawContactId3); 524 525 String displayName2 = queryDisplayName(queryContactId(rawContactId1)); 526 assertEquals("Johni Smithi", displayName2); 527 528 String displayName3 = queryDisplayName(queryContactId(rawContactId2)); 529 assertEquals("Johnj Smithj", displayName3); 530 531 String displayName4 = queryDisplayName(queryContactId(rawContactId3)); 532 assertEquals("Johnm Smithm", displayName4); 533 } 534 535 public void testAggregationExceptionKeepOutCheckResultDisplayNames() { 536 long rawContactId1 = createRawContactWithName("c", "c", ACCOUNT_1); 537 long rawContactId2 = createRawContactWithName("b", "b", ACCOUNT_2); 538 long rawContactId3 = createRawContactWithName("a", "a", ACCOUNT_3); 539 540 // Join all contacts 541 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 542 rawContactId1, rawContactId2); 543 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 544 rawContactId1, rawContactId3); 545 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 546 rawContactId2, rawContactId3); 547 548 // Separate all contacts. The order (2-3 , 1-2, 1-3) is important 549 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 550 rawContactId2, rawContactId3); 551 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 552 rawContactId1, rawContactId2); 553 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 554 rawContactId1, rawContactId3); 555 556 // Verify that we have three different contacts 557 long contactId1 = queryContactId(rawContactId1); 558 long contactId2 = queryContactId(rawContactId2); 559 long contactId3 = queryContactId(rawContactId3); 560 561 assertTrue(contactId1 != contactId2); 562 assertTrue(contactId1 != contactId3); 563 assertTrue(contactId2 != contactId3); 564 565 // Verify that each raw contact contribute to the contact display name 566 assertDisplayNameEquals(contactId1, rawContactId1); 567 assertDisplayNameEquals(contactId2, rawContactId2); 568 assertDisplayNameEquals(contactId3, rawContactId3); 569 } 570 571 public void testNonAggregationWithMultipleAffinities() { 572 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 573 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 574 assertNotAggregated(rawContactId1, rawContactId2); 575 576 // There are two aggregates this raw contact could join, so it should join neither 577 long rawContactId3 = createRawContactWithName("John", "Doe", ACCOUNT_2); 578 assertNotAggregated(rawContactId1, rawContactId3); 579 assertNotAggregated(rawContactId2, rawContactId3); 580 581 // Just in case - let's make sure the original two did not get aggregated in the process 582 assertNotAggregated(rawContactId1, rawContactId2); 583 } 584 585 public void testSplitBecauseOfMultipleAffinities() { 586 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 587 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_2); 588 assertAggregated(rawContactId1, rawContactId2); 589 590 // The aggregate this raw contact could join has a raw contact from the same account, 591 // let's not aggregate and break up the existing aggregate because of the ambiguity 592 long rawContactId3 = createRawContactWithName("John", "Doe", ACCOUNT_1); 593 assertNotAggregated(rawContactId1, rawContactId3); 594 assertNotAggregated(rawContactId2, rawContactId3); 595 assertNotAggregated(rawContactId1, rawContactId2); 596 } 597 598 public void testReaggregationWhenBecomesInvisible() { 599 Account account = new Account("accountName", "accountType"); 600 createAutoAddGroup(account); 601 602 long rawContactId1 = createRawContact(account); 603 insertStructuredName(rawContactId1, "Flynn", "Ryder"); 604 605 long rawContactId2 = createRawContact(ACCOUNT_2); 606 insertStructuredName(rawContactId2, "Flynn", "Ryder"); 607 608 long rawContactId3 = createRawContact(account); 609 insertStructuredName(rawContactId3, "Flynn", "Ryder"); 610 611 assertNotAggregated(rawContactId1, rawContactId3); 612 assertNotAggregated(rawContactId2, rawContactId3); 613 assertNotAggregated(rawContactId1, rawContactId2); 614 615 // Hide by removing from all groups 616 removeGroupMemberships(rawContactId3); 617 618 assertAggregated(rawContactId1, rawContactId2, "Flynn Ryder"); 619 assertNotAggregated(rawContactId1, rawContactId3); 620 assertNotAggregated(rawContactId2, rawContactId3); 621 } 622 623 public void testReaggregationWhenBecomesInvisibleSecondaryDataMatch() { 624 Account account = new Account("accountName", "accountType"); 625 createAutoAddGroup(account); 626 627 long rawContactId1 = createRawContact(account); 628 insertStructuredName(rawContactId1, "Flynn", "Ryder"); 629 insertPhoneNumber(rawContactId1, "1234567890"); 630 631 long rawContactId2 = createRawContact(ACCOUNT_2); 632 insertPhoneNumber(rawContactId2, "1234567890"); 633 634 long rawContactId3 = createRawContact(account); 635 insertStructuredName(rawContactId3, "Flynn", "Ryder"); 636 637 assertNotAggregated(rawContactId1, rawContactId3); 638 assertNotAggregated(rawContactId2, rawContactId3); 639 assertNotAggregated(rawContactId1, rawContactId2); 640 641 // Hide by removing from all groups 642 removeGroupMemberships(rawContactId3); 643 644 assertAggregated(rawContactId1, rawContactId2, "Flynn Ryder"); 645 assertNotAggregated(rawContactId1, rawContactId3); 646 assertNotAggregated(rawContactId2, rawContactId3); 647 } 648 649 public void testReaggregationWhenBecomesVisible() { 650 Account account = new Account("accountName", "accountType"); 651 long groupId = createAutoAddGroup(account); 652 653 long rawContactId1 = createRawContact(account); 654 insertStructuredName(rawContactId1, "Flynn", "Ryder"); 655 656 long rawContactId2 = createRawContact(ACCOUNT_2); 657 insertStructuredName(rawContactId2, "Flynn", "Ryder"); 658 659 long rawContactId3 = createRawContact(account); 660 removeGroupMemberships(rawContactId3); 661 insertStructuredName(rawContactId3, "Flynn", "Ryder"); 662 663 assertAggregated(rawContactId1, rawContactId2, "Flynn Ryder"); 664 assertNotAggregated(rawContactId1, rawContactId3); 665 assertNotAggregated(rawContactId2, rawContactId3); 666 667 insertGroupMembership(rawContactId3, groupId); 668 669 assertNotAggregated(rawContactId1, rawContactId3); 670 assertNotAggregated(rawContactId2, rawContactId3); 671 assertNotAggregated(rawContactId1, rawContactId2); 672 } 673 674 public void testNonSplitBecauseOfMultipleAffinitiesWhenOverridden() { 675 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 676 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_2); 677 long rawContactId3 = createRawContactWithName("John", "Doe", ACCOUNT_3); 678 assertAggregated(rawContactId1, rawContactId2); 679 assertAggregated(rawContactId1, rawContactId3); 680 setAggregationException( 681 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 682 assertAggregated(rawContactId1, rawContactId2); 683 assertAggregated(rawContactId1, rawContactId3); 684 685 // The aggregate this raw contact could join has a raw contact from the same account, 686 // let's not aggregate and break up the existing aggregate because of the ambiguity 687 long rawContactId4 = createRawContactWithName("John", "Doe", ACCOUNT_1); 688 assertAggregated(rawContactId1, rawContactId2); // Aggregation exception 689 assertNotAggregated(rawContactId1, rawContactId3); 690 assertNotAggregated(rawContactId1, rawContactId4); 691 assertNotAggregated(rawContactId3, rawContactId4); 692 } 693 694 public void testNonAggregationFromSameAccount() { 695 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 696 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 697 assertNotAggregated(rawContactId1, rawContactId2); 698 } 699 700 public void testNonAggregationFromSameAccountNoCommonData() { 701 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 702 insertEmail(rawContactId1, "lightning1 (at) android.com"); 703 insertPhoneNumber(rawContactId1, "111-222-3333"); 704 insertIdentity(rawContactId1, "iden1", "namespace"); 705 706 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 707 insertEmail(rawContactId2, "lightning2 (at) android.com"); 708 insertPhoneNumber(rawContactId2, "555-666-7777"); 709 insertIdentity(rawContactId1, "iden2", "namespace"); 710 711 assertNotAggregated(rawContactId1, rawContactId2); 712 } 713 714 public void testAggregationFromSameAccountEmailSame() { 715 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 716 insertEmail(rawContactId1, "lightning (at) android.com"); 717 718 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 719 insertEmail(rawContactId2, "lightning (at) android.com"); 720 721 assertAggregated(rawContactId1, rawContactId2); 722 } 723 724 public void testNonAggregationFromSameAccountEmailDifferent() { 725 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 726 insertEmail(rawContactId1, "lightning1 (at) android.com"); 727 728 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 729 insertEmail(rawContactId2, "lightning2 (at) android.com"); 730 insertEmail(rawContactId2, "lightning3 (at) android.com"); 731 732 assertNotAggregated(rawContactId1, rawContactId2); 733 } 734 735 public void testAggregationFromSameAccountIdentitySame() { 736 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 737 insertIdentity(rawContactId1, "iden", "namespace"); 738 739 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 740 insertIdentity(rawContactId2, "iden", "namespace"); 741 742 assertAggregated(rawContactId1, rawContactId2); 743 } 744 745 public void testNonAggregationFromSameAccountIdentityDifferent() { 746 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 747 insertIdentity(rawContactId1, "iden1", "namespace1"); 748 insertIdentity(rawContactId1, "iden2", "namespace2"); 749 750 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 751 insertIdentity(rawContactId2, "iden2", "namespace1"); 752 insertIdentity(rawContactId2, "iden1", "namespace2"); 753 754 assertNotAggregated(rawContactId1, rawContactId2); 755 } 756 757 public void testAggregationFromSameAccountPhoneNumberSame() { 758 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 759 insertPhoneNumber(rawContactId1, "111-222-3333"); 760 761 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 762 insertPhoneNumber(rawContactId2, "111-222-3333"); 763 764 assertAggregated(rawContactId1, rawContactId2); 765 } 766 767 public void testAggregationFromSameAccountPhoneNumberNormalizedSame() { 768 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 769 insertPhoneNumber(rawContactId1, "111-222-3333"); 770 771 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 772 insertPhoneNumber(rawContactId2, "+1-111-222-3333"); 773 774 assertAggregated(rawContactId1, rawContactId2); 775 } 776 777 public void testNonAggregationFromSameAccountPhoneNumberDifferent() { 778 long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1); 779 insertPhoneNumber(rawContactId1, "111-222-3333"); 780 781 long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_1); 782 insertPhoneNumber(rawContactId2, "111-222-3334"); 783 784 assertNotAggregated(rawContactId1, rawContactId2); 785 } 786 787 public void testAggregationSuggestionsBasedOnName() { 788 long rawContactId1 = createRawContact(); 789 insertStructuredName(rawContactId1, "Duane", null); 790 791 // Exact name match 792 long rawContactId2 = createRawContact(); 793 insertStructuredName(rawContactId2, "Duane", null); 794 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 795 rawContactId1, rawContactId2); 796 797 // Edit distance == 0.84 798 long rawContactId3 = createRawContact(); 799 insertStructuredName(rawContactId3, "Dwayne", null); 800 801 // Edit distance == 0.6 802 long rawContactId4 = createRawContact(); 803 insertStructuredName(rawContactId4, "Donny", null); 804 805 long contactId1 = queryContactId(rawContactId1); 806 long contactId2 = queryContactId(rawContactId2); 807 long contactId3 = queryContactId(rawContactId3); 808 809 assertSuggestions(contactId1, contactId2, contactId3); 810 } 811 812 public void testAggregationSuggestionsBasedOnPhoneNumber() { 813 814 // Create two contacts that would not be aggregated because of name mismatch 815 long rawContactId1 = createRawContact(); 816 insertStructuredName(rawContactId1, "Lord", "Farquaad"); 817 insertPhoneNumber(rawContactId1, "(888)555-1236"); 818 819 long rawContactId2 = createRawContact(); 820 insertStructuredName(rawContactId2, "Talking", "Donkey"); 821 insertPhoneNumber(rawContactId2, "1(888)555-1236"); 822 823 long contactId1 = queryContactId(rawContactId1); 824 long contactId2 = queryContactId(rawContactId2); 825 assertTrue(contactId1 != contactId2); 826 827 assertSuggestions(contactId1, contactId2); 828 } 829 830 public void testAggregationSuggestionsBasedOnEmailAddress() { 831 832 // Create two contacts that would not be aggregated because of name mismatch 833 long rawContactId1 = createRawContact(); 834 insertStructuredName(rawContactId1, "Carl", "Fredricksen"); 835 insertEmail(rawContactId1, "up (at) android.com"); 836 837 long rawContactId2 = createRawContact(); 838 insertStructuredName(rawContactId2, "Charles", "Muntz"); 839 insertEmail(rawContactId2, "up (at) android.com"); 840 841 long contactId1 = queryContactId(rawContactId1); 842 long contactId2 = queryContactId(rawContactId2); 843 assertTrue(contactId1 != contactId2); 844 845 assertSuggestions(contactId1, contactId2); 846 } 847 848 public void testAggregationSuggestionsBasedOnEmailAddressApproximateMatch() { 849 850 // Create two contacts that would not be aggregated because of name mismatch 851 long rawContactId1 = createRawContact(); 852 insertStructuredName(rawContactId1, "Bob", null); 853 insertEmail(rawContactId1, "incredible (at) android.com"); 854 855 long rawContactId2 = createRawContact(); 856 insertStructuredName(rawContactId2, "Lucius", "Best"); 857 insertEmail(rawContactId2, "incrediball (at) android.com"); 858 859 long contactId1 = queryContactId(rawContactId1); 860 long contactId2 = queryContactId(rawContactId2); 861 assertTrue(contactId1 != contactId2); 862 863 assertSuggestions(contactId1, contactId2); 864 } 865 866 public void testAggregationSuggestionsBasedOnNickname() { 867 long rawContactId1 = createRawContact(); 868 insertStructuredName(rawContactId1, "Peter", "Parker"); 869 insertNickname(rawContactId1, "Spider-Man"); 870 871 long rawContactId2 = createRawContact(); 872 insertStructuredName(rawContactId2, "Manny", "Spider"); 873 874 long contactId1 = queryContactId(rawContactId1); 875 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 876 rawContactId1, rawContactId2); 877 878 long contactId2 = queryContactId(rawContactId2); 879 assertSuggestions(contactId1, contactId2); 880 } 881 882 public void testAggregationSuggestionsBasedOnNicknameMatchingName() { 883 long rawContactId1 = createRawContact(); 884 insertStructuredName(rawContactId1, "Clark", "Kent"); 885 insertNickname(rawContactId1, "Superman"); 886 887 long rawContactId2 = createRawContact(); 888 insertStructuredName(rawContactId2, "Roy", "Williams"); 889 insertNickname(rawContactId2, "superman"); 890 891 long contactId1 = queryContactId(rawContactId1); 892 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 893 rawContactId1, rawContactId2); 894 895 long contactId2 = queryContactId(rawContactId2); 896 assertSuggestions(contactId1, contactId2); 897 } 898 899 public void testAggregationSuggestionsBasedOnCommonNickname() { 900 long rawContactId1 = createRawContact(); 901 insertStructuredName(rawContactId1, "Dick", "Cherry"); 902 903 long rawContactId2 = createRawContact(); 904 insertStructuredName(rawContactId2, "Richard", "Cherry"); 905 906 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 907 rawContactId1, rawContactId2); 908 909 long contactId1 = queryContactId(rawContactId1); 910 long contactId2 = queryContactId(rawContactId2); 911 assertSuggestions(contactId1, contactId2); 912 } 913 914 public void testAggregationSuggestionsBasedOnPhoneNumberWithFilter() { 915 916 // Create two contacts that would not be aggregated because of name mismatch 917 long rawContactId1 = createRawContact(); 918 insertStructuredName(rawContactId1, "Lord", "Farquaad"); 919 insertPhoneNumber(rawContactId1, "(888)555-1236"); 920 921 long rawContactId2 = createRawContact(); 922 insertStructuredName(rawContactId2, "Talking", "Donkey"); 923 insertPhoneNumber(rawContactId2, "1(888)555-1236"); 924 925 long contactId1 = queryContactId(rawContactId1); 926 long contactId2 = queryContactId(rawContactId2); 927 assertTrue(contactId1 != contactId2); 928 929 assertSuggestions(contactId1, "talk", contactId2); 930 assertSuggestions(contactId1, "don", contactId2); 931 assertSuggestions(contactId1, "", contactId2); 932 assertSuggestions(contactId1, "eddie"); 933 } 934 935 public void testAggregationSuggestionsDontSuggestInvisible() { 936 long rawContactId1 = createRawContactWithName("first", "last", ACCOUNT_1); 937 insertPhoneNumber(rawContactId1, "111-222-3333"); 938 insertNickname(rawContactId1, "Superman"); 939 insertEmail(rawContactId1, "incredible (at) android.com"); 940 941 // Create another with the exact same name, phone number, nickname and email. 942 long rawContactId2 = createRawContactWithName("first", "last", ACCOUNT_2); 943 insertPhoneNumber(rawContactId2, "111-222-3333"); 944 insertNickname(rawContactId2, "Superman"); 945 insertEmail(rawContactId2, "incredible (at) android.com"); 946 947 // The aggregator should have joined them. Split them up. 948 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 949 rawContactId1, rawContactId2); 950 951 long contactId1 = queryContactId(rawContactId1); 952 long contactId2 = queryContactId(rawContactId2); 953 954 // Make sure they're different contacts. 955 MoreAsserts.assertNotEqual(contactId1, contactId2); 956 957 // Contact 2 should be suggested. 958 assertSuggestions(contactId1, contactId2); 959 960 // Make contact 2 invisible. 961 markInvisible(contactId2); 962 963 // Now contact 2 shuldn't be suggested. 964 assertSuggestions(contactId1, new long[0]); 965 } 966 967 public void testChoosePhotoSetBeforeAggregation() { 968 long rawContactId1 = createRawContact(); 969 setContactAccount(rawContactId1, "donut", "donut_act"); 970 insertPhoto(rawContactId1); 971 972 long rawContactId2 = createRawContact(); 973 setContactAccount(rawContactId2, "cupcake", "cupcake_act"); 974 long cupcakeId = ContentUris.parseId(insertPhoto(rawContactId2)); 975 976 long rawContactId3 = createRawContact(); 977 setContactAccount(rawContactId3, "froyo", "froyo_act"); 978 insertPhoto(rawContactId3); 979 980 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 981 rawContactId1, rawContactId2); 982 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 983 rawContactId1, rawContactId3); 984 assertEquals(cupcakeId, queryPhotoId(queryContactId(rawContactId2))); 985 } 986 987 public void testChoosePhotoSetAfterAggregation() { 988 long rawContactId1 = createRawContact(); 989 setContactAccount(rawContactId1, "donut", "donut_act"); 990 insertPhoto(rawContactId1); 991 992 long rawContactId2 = createRawContact(); 993 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 994 rawContactId1, rawContactId2); 995 setContactAccount(rawContactId2, "cupcake", "cupcake_act"); 996 long cupcakeId = ContentUris.parseId(insertPhoto(rawContactId2)); 997 998 long rawContactId3 = createRawContact(); 999 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 1000 rawContactId1, rawContactId3); 1001 setContactAccount(rawContactId3, "froyo", "froyo_act"); 1002 insertPhoto(rawContactId3); 1003 1004 assertEquals(cupcakeId, queryPhotoId(queryContactId(rawContactId2))); 1005 } 1006 1007 // Note that for the following tests of photo aggregation, the accounts are being used to 1008 // set the typical photo priority that each raw contact would have, based on 1009 // SynchronousContactsProvider2.createPhotoPriorityResolver. The relative priorities 1010 // specified there are: 1011 // cupcake: 3 1012 // donut: 2 1013 // froyo: 1 1014 // <other>: 0 1015 1016 public void testChooseLargerPhotoByDimensions() { 1017 // Donut photo is 256x256. 1018 long rawContactId1 = createRawContact(); 1019 setContactAccount(rawContactId1, "donut", "donut_act"); 1020 long normalEarthDataId = ContentUris.parseId( 1021 insertPhoto(rawContactId1, R.drawable.earth_normal)); 1022 long normalEarthPhotoFileId = getStoredLongValue( 1023 ContentUris.withAppendedId(Data.CONTENT_URI, normalEarthDataId), 1024 Photo.PHOTO_FILE_ID); 1025 1026 // Cupcake would normally have priority, but its photo is 200x200. 1027 long rawContactId2 = createRawContact(); 1028 setContactAccount(rawContactId2, "cupcake", "cupcake_act"); 1029 insertPhoto(rawContactId2, R.drawable.earth_200); 1030 1031 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 1032 rawContactId1, rawContactId2); 1033 1034 // Larger photo (by dimensions) wins. 1035 assertEquals(normalEarthPhotoFileId, queryPhotoFileId(queryContactId(rawContactId1))); 1036 } 1037 1038 public void testChooseLargerPhotoByFileSize() { 1039 // Donut photo is a 256x256 photo of a nebula. 1040 long rawContactId1 = createRawContact(); 1041 setContactAccount(rawContactId1, "donut", "donut_act"); 1042 long nebulaDataId = ContentUris.parseId( 1043 insertPhoto(rawContactId1, R.drawable.nebula)); 1044 long nebulaPhotoFileId = getStoredLongValue( 1045 ContentUris.withAppendedId(Data.CONTENT_URI, nebulaDataId), 1046 Photo.PHOTO_FILE_ID); 1047 1048 // Cupcake would normally have priority, but its photo (of a galaxy) has the same dimensions 1049 // as Donut's, but a smaller filesize. 1050 long rawContactId2 = createRawContact(); 1051 setContactAccount(rawContactId2, "cupcake", "cupcake_act"); 1052 insertPhoto(rawContactId2, R.drawable.galaxy); 1053 1054 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 1055 rawContactId1, rawContactId2); 1056 1057 // Larger photo (by filesize) wins. 1058 assertEquals(nebulaPhotoFileId, queryPhotoFileId(queryContactId(rawContactId1))); 1059 } 1060 1061 public void testChooseFilePhotoOverThumbnail() { 1062 // Donut photo is a 256x256 photo of Earth. 1063 long rawContactId1 = createRawContact(); 1064 setContactAccount(rawContactId1, "donut", "donut_act"); 1065 long normalEarthDataId = ContentUris.parseId( 1066 insertPhoto(rawContactId1, R.drawable.earth_normal)); 1067 long normalEarthPhotoFileId = getStoredLongValue( 1068 ContentUris.withAppendedId(Data.CONTENT_URI, normalEarthDataId), 1069 Photo.PHOTO_FILE_ID); 1070 1071 // Cupcake would normally have priority, but its photo of Earth is thumbnail-sized. 1072 long rawContactId2 = createRawContact(); 1073 setContactAccount(rawContactId2, "cupcake", "cupcake_act"); 1074 insertPhoto(rawContactId2, R.drawable.earth_small); 1075 1076 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 1077 rawContactId1, rawContactId2); 1078 1079 // Larger photo (by filesize) wins. 1080 assertEquals(normalEarthPhotoFileId, queryPhotoFileId(queryContactId(rawContactId1))); 1081 } 1082 1083 public void testFallbackToAccountPriorityForSamePhoto() { 1084 // Donut photo is a 256x256 photo of Earth. 1085 long rawContactId1 = createRawContact(); 1086 setContactAccount(rawContactId1, "donut", "donut_act"); 1087 insertPhoto(rawContactId1, R.drawable.earth_normal); 1088 1089 // Cupcake has the same 256x256 photo of Earth. 1090 long rawContactId2 = createRawContact(); 1091 setContactAccount(rawContactId2, "cupcake", "cupcake_act"); 1092 long cupcakeEarthDataId = ContentUris.parseId( 1093 insertPhoto(rawContactId2, R.drawable.earth_normal)); 1094 long cupcakeEarthPhotoFileId = getStoredLongValue( 1095 ContentUris.withAppendedId(Data.CONTENT_URI, cupcakeEarthDataId), 1096 Photo.PHOTO_FILE_ID); 1097 1098 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 1099 rawContactId1, rawContactId2); 1100 1101 // Cupcake's version of the photo wins, falling back to account priority. 1102 assertEquals(cupcakeEarthPhotoFileId, queryPhotoFileId(queryContactId(rawContactId1))); 1103 } 1104 1105 public void testFallbackToAccountPriorityForDifferingThumbnails() { 1106 // Donut photo is a 96x96 thumbnail of Earth. 1107 long rawContactId1 = createRawContact(); 1108 setContactAccount(rawContactId1, "donut", "donut_act"); 1109 insertPhoto(rawContactId1, R.drawable.earth_small); 1110 1111 // Cupcake photo is the 96x96 "no contact" placeholder (smaller filesize than the Earth 1112 // picture, but thumbnail filesizes are ignored in the aggregator). 1113 long rawContactId2 = createRawContact(); 1114 setContactAccount(rawContactId2, "cupcake", "cupcake_act"); 1115 long cupcakeDataId = ContentUris.parseId( 1116 insertPhoto(rawContactId2, R.drawable.ic_contact_picture)); 1117 1118 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 1119 rawContactId1, rawContactId2); 1120 1121 // The Cupcake thumbnail wins, by account priority.. 1122 assertEquals(cupcakeDataId, queryPhotoId(queryContactId(rawContactId1))); 1123 } 1124 1125 public void testDisplayNameSources() { 1126 long rawContactId = createRawContact(); 1127 long contactId = queryContactId(rawContactId); 1128 1129 assertNull(queryDisplayName(contactId)); 1130 1131 insertEmail(rawContactId, "eclair (at) android.com"); 1132 assertEquals("eclair (at) android.com", queryDisplayName(contactId)); 1133 1134 insertPhoneNumber(rawContactId, "800-555-5555"); 1135 assertEquals("800-555-5555", queryDisplayName(contactId)); 1136 1137 ContentValues values = new ContentValues(); 1138 values.put(Organization.COMPANY, "Android"); 1139 insertOrganization(rawContactId, values); 1140 assertEquals("Android", queryDisplayName(contactId)); 1141 1142 insertNickname(rawContactId, "Dro"); 1143 assertEquals("Dro", queryDisplayName(contactId)); 1144 1145 values.clear(); 1146 values.put(StructuredName.GIVEN_NAME, "Eclair"); 1147 values.put(StructuredName.FAMILY_NAME, "Android"); 1148 insertStructuredName(rawContactId, values); 1149 assertEquals("Eclair Android", queryDisplayName(contactId)); 1150 } 1151 1152 public void testVerifiedName() { 1153 long rawContactId1 = createRawContactWithName("test1", "TEST1", ACCOUNT_1); 1154 storeValue(RawContacts.CONTENT_URI, rawContactId1, RawContacts.NAME_VERIFIED, "1"); 1155 long rawContactId2 = createRawContactWithName("test2", "TEST2", ACCOUNT_2); 1156 long rawContactId3 = createRawContactWithName("test3", "TEST3 LONG", ACCOUNT_3); 1157 1158 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, 1159 rawContactId2); 1160 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, 1161 rawContactId3); 1162 1163 long contactId = queryContactId(rawContactId1); 1164 1165 // Should be the verified name 1166 assertEquals("test1 TEST1", queryDisplayName(contactId)); 1167 1168 // Mark a different name as verified - this should reset the NAME_VERIFIED field 1169 // for the other rawContacts 1170 storeValue(RawContacts.CONTENT_URI, rawContactId2, RawContacts.NAME_VERIFIED, "1"); 1171 assertStoredValue(RawContacts.CONTENT_URI, rawContactId1, RawContacts.NAME_VERIFIED, 0); 1172 assertEquals("test2 TEST2", queryDisplayName(contactId)); 1173 1174 // Reset the NAME_VERIFIED flag - now the most complex of the three names should win 1175 storeValue(RawContacts.CONTENT_URI, rawContactId2, RawContacts.NAME_VERIFIED, "0"); 1176 assertEquals("test3 TEST3 LONG", queryDisplayName(contactId)); 1177 } 1178 1179 public void testAggregationModeSuspendedSeparateTransactions() { 1180 1181 // Setting aggregation mode to SUSPENDED should prevent aggregation from happening 1182 long rawContactId1 = createRawContact(ACCOUNT_1); 1183 storeValue(RawContacts.CONTENT_URI, rawContactId1, 1184 RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED); 1185 Uri name1 = insertStructuredName(rawContactId1, "THE", "SAME"); 1186 1187 long rawContactId2 = createRawContact(ACCOUNT_2); 1188 storeValue(RawContacts.CONTENT_URI, rawContactId2, 1189 RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED); 1190 insertStructuredName(rawContactId2, "THE", "SAME"); 1191 1192 assertNotAggregated(rawContactId1, rawContactId2); 1193 1194 // Changing aggregation mode to DEFAULT should change nothing 1195 storeValue(RawContacts.CONTENT_URI, rawContactId1, 1196 RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT); 1197 storeValue(RawContacts.CONTENT_URI, rawContactId2, 1198 RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT); 1199 assertNotAggregated(rawContactId1, rawContactId2); 1200 1201 // Changing the name should trigger aggregation 1202 storeValue(name1, StructuredName.GIVEN_NAME, "the"); 1203 assertAggregated(rawContactId1, rawContactId2); 1204 } 1205 1206 public void testAggregationModeInitializedAsSuspended() throws Exception { 1207 1208 // Setting aggregation mode to SUSPENDED should prevent aggregation from happening 1209 ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 1210 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 1211 .build(); 1212 ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 1213 .withValueBackReference(Data.RAW_CONTACT_ID, 0) 1214 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 1215 .withValue(StructuredName.GIVEN_NAME, "John") 1216 .withValue(StructuredName.FAMILY_NAME, "Doe") 1217 .build(); 1218 ContentProviderOperation cpo3 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 1219 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 1220 .build(); 1221 ContentProviderOperation cpo4 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 1222 .withValueBackReference(Data.RAW_CONTACT_ID, 2) 1223 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 1224 .withValue(StructuredName.GIVEN_NAME, "John") 1225 .withValue(StructuredName.FAMILY_NAME, "Doe") 1226 .build(); 1227 ContentProviderOperation cpo5 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 1228 .withSelection(RawContacts._ID + "=?", new String[1]) 1229 .withSelectionBackReference(0, 0) 1230 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT) 1231 .build(); 1232 ContentProviderOperation cpo6 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 1233 .withSelection(RawContacts._ID + "=?", new String[1]) 1234 .withSelectionBackReference(0, 2) 1235 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT) 1236 .build(); 1237 1238 ContentProviderResult[] results = 1239 mResolver.applyBatch(ContactsContract.AUTHORITY, 1240 Lists.newArrayList(cpo1, cpo2, cpo3, cpo4, cpo5, cpo6)); 1241 1242 long rawContactId1 = ContentUris.parseId(results[0].uri); 1243 long rawContactId2 = ContentUris.parseId(results[2].uri); 1244 1245 assertNotAggregated(rawContactId1, rawContactId2); 1246 } 1247 1248 public void testAggregationModeUpdatedToSuspended() throws Exception { 1249 1250 // Setting aggregation mode to SUSPENDED should prevent aggregation from happening 1251 ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 1252 .withValues(new ContentValues()) 1253 .build(); 1254 ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 1255 .withValueBackReference(Data.RAW_CONTACT_ID, 0) 1256 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 1257 .withValue(StructuredName.GIVEN_NAME, "John") 1258 .withValue(StructuredName.FAMILY_NAME, "Doe") 1259 .build(); 1260 ContentProviderOperation cpo3 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 1261 .withValues(new ContentValues()) 1262 .build(); 1263 ContentProviderOperation cpo4 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 1264 .withValueBackReference(Data.RAW_CONTACT_ID, 2) 1265 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 1266 .withValue(StructuredName.GIVEN_NAME, "John") 1267 .withValue(StructuredName.FAMILY_NAME, "Doe") 1268 .build(); 1269 ContentProviderOperation cpo5 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 1270 .withSelection(RawContacts._ID + "=?", new String[1]) 1271 .withSelectionBackReference(0, 0) 1272 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 1273 .build(); 1274 ContentProviderOperation cpo6 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 1275 .withSelection(RawContacts._ID + "=?", new String[1]) 1276 .withSelectionBackReference(0, 2) 1277 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 1278 .build(); 1279 1280 ContentProviderResult[] results = 1281 mResolver.applyBatch(ContactsContract.AUTHORITY, 1282 Lists.newArrayList(cpo1, cpo2, cpo3, cpo4, cpo5, cpo6)); 1283 1284 long rawContactId1 = ContentUris.parseId(results[0].uri); 1285 long rawContactId2 = ContentUris.parseId(results[2].uri); 1286 1287 assertNotAggregated(rawContactId1, rawContactId2); 1288 } 1289 1290 public void testAggregationModeSuspendedOverriddenByAggException() throws Exception { 1291 ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 1292 .withValue(RawContacts.ACCOUNT_NAME, "a") 1293 .withValue(RawContacts.ACCOUNT_TYPE, "b") 1294 .build(); 1295 ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 1296 .withValueBackReference(Data.RAW_CONTACT_ID, 0) 1297 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 1298 .withValue(StructuredName.GIVEN_NAME, "John") 1299 .withValue(StructuredName.FAMILY_NAME, "Doe") 1300 .build(); 1301 ContentProviderOperation cpo3 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 1302 .withValue(RawContacts.ACCOUNT_NAME, "c") 1303 .withValue(RawContacts.ACCOUNT_TYPE, "d") 1304 .build(); 1305 ContentProviderOperation cpo4 = ContentProviderOperation.newInsert(Data.CONTENT_URI) 1306 .withValueBackReference(Data.RAW_CONTACT_ID, 2) 1307 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) 1308 .withValue(StructuredName.GIVEN_NAME, "John") 1309 .withValue(StructuredName.FAMILY_NAME, "Doe") 1310 .build(); 1311 ContentProviderOperation cpo5 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 1312 .withSelection(RawContacts._ID + "=?", new String[1]) 1313 .withSelectionBackReference(0, 0) 1314 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 1315 .build(); 1316 ContentProviderOperation cpo6 = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI) 1317 .withSelection(RawContacts._ID + "=?", new String[1]) 1318 .withSelectionBackReference(0, 2) 1319 .withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED) 1320 .build(); 1321 1322 // Checking that aggregation mode SUSPENDED should be overridden by inserting 1323 // an explicit aggregation exception 1324 ContentProviderOperation cpo7 = 1325 ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI) 1326 .withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1, 0) 1327 .withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, 2) 1328 .withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER) 1329 .build(); 1330 1331 ContentProviderResult[] results = 1332 mResolver.applyBatch(ContactsContract.AUTHORITY, 1333 Lists.newArrayList(cpo1, cpo2, cpo3, cpo4, cpo5, cpo6, cpo7)); 1334 1335 long rawContactId1 = ContentUris.parseId(results[0].uri); 1336 long rawContactId2 = ContentUris.parseId(results[2].uri); 1337 1338 assertAggregated(rawContactId1, rawContactId2); 1339 } 1340 1341 public void testAggregationSuggestionsQueryBuilderWithContactId() throws Exception { 1342 Uri uri = AggregationSuggestions.builder().setContactId(12).setLimit(7).build(); 1343 assertEquals("content://com.android.contacts/contacts/12/suggestions?limit=7", 1344 uri.toString()); 1345 } 1346 1347 public void testAggregationSuggestionsQueryBuilderWithValues() throws Exception { 1348 Uri uri = AggregationSuggestions.builder() 1349 .addParameter(AggregationSuggestions.PARAMETER_MATCH_NAME, "name1") 1350 .addParameter(AggregationSuggestions.PARAMETER_MATCH_NAME, "name2") 1351 .addParameter(AggregationSuggestions.PARAMETER_MATCH_EMAIL, "email1") 1352 .addParameter(AggregationSuggestions.PARAMETER_MATCH_EMAIL, "email2") 1353 .addParameter(AggregationSuggestions.PARAMETER_MATCH_PHONE, "phone1") 1354 .addParameter(AggregationSuggestions.PARAMETER_MATCH_NICKNAME, "nickname1") 1355 .setLimit(7) 1356 .build(); 1357 assertEquals("content://com.android.contacts/contacts/0/suggestions?" 1358 + "limit=7" 1359 + "&query=name%3Aname1" 1360 + "&query=name%3Aname2" 1361 + "&query=email%3Aemail1" 1362 + "&query=email%3Aemail2" 1363 + "&query=phone%3Aphone1" 1364 + "&query=nickname%3Anickname1", uri.toString()); 1365 } 1366 1367 public void testAggregatedStatusUpdate() { 1368 long rawContactId1 = createRawContact(); 1369 Uri dataUri1 = insertStructuredName(rawContactId1, "john", "doe"); 1370 insertStatusUpdate(ContentUris.parseId(dataUri1), StatusUpdates.AWAY, "Green", 100, 1371 StatusUpdates.CAPABILITY_HAS_CAMERA); 1372 long rawContactId2 = createRawContact(); 1373 Uri dataUri2 = insertStructuredName(rawContactId2, "john", "doe"); 1374 insertStatusUpdate(ContentUris.parseId(dataUri2), StatusUpdates.AVAILABLE, "Red", 50, 1375 StatusUpdates.CAPABILITY_HAS_CAMERA); 1376 setAggregationException( 1377 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 1378 1379 assertStoredValue(getContactUriForRawContact(rawContactId1), 1380 Contacts.CONTACT_STATUS, "Green"); 1381 1382 // When we split these two raw contacts, their respective statuses should be restored 1383 setAggregationException( 1384 AggregationExceptions.TYPE_KEEP_SEPARATE, rawContactId1, rawContactId2); 1385 1386 assertStoredValue(getContactUriForRawContact(rawContactId1), 1387 Contacts.CONTACT_STATUS, "Green"); 1388 1389 assertStoredValue(getContactUriForRawContact(rawContactId2), 1390 Contacts.CONTACT_STATUS, "Red"); 1391 } 1392 1393 public void testAggregationSuggestionsByName() throws Exception { 1394 long rawContactId1 = createRawContactWithName("first1", "last1"); 1395 long rawContactId2 = createRawContactWithName("first2", "last2"); 1396 1397 Uri uri = AggregationSuggestions.builder() 1398 .addParameter(AggregationSuggestions.PARAMETER_MATCH_NAME, "last1 first1") 1399 .build(); 1400 1401 Cursor cursor = mResolver.query( 1402 uri, new String[] { Contacts._ID, Contacts.DISPLAY_NAME }, null, null, null); 1403 1404 assertEquals(1, cursor.getCount()); 1405 1406 cursor.moveToFirst(); 1407 1408 ContentValues values = new ContentValues(); 1409 values.put(Contacts._ID, queryContactId(rawContactId1)); 1410 values.put(Contacts.DISPLAY_NAME, "first1 last1"); 1411 assertCursorValues(cursor, values); 1412 cursor.close(); 1413 } 1414 1415 private void assertSuggestions(long contactId, long... suggestions) { 1416 final Uri aggregateUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 1417 Uri uri = Uri.withAppendedPath(aggregateUri, 1418 Contacts.AggregationSuggestions.CONTENT_DIRECTORY); 1419 assertSuggestions(uri, suggestions); 1420 } 1421 1422 private void assertSuggestions(long contactId, String filter, long... suggestions) { 1423 final Uri aggregateUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 1424 Uri uri = Uri.withAppendedPath(Uri.withAppendedPath(aggregateUri, 1425 Contacts.AggregationSuggestions.CONTENT_DIRECTORY), Uri.encode(filter)); 1426 assertSuggestions(uri, suggestions); 1427 } 1428 1429 private void assertSuggestions(Uri uri, long... suggestions) { 1430 final Cursor cursor = mResolver.query(uri, 1431 new String[] { Contacts._ID, Contacts.CONTACT_PRESENCE }, 1432 null, null, null); 1433 1434 try { 1435 assertEquals(suggestions.length, cursor.getCount()); 1436 1437 for (int i = 0; i < suggestions.length; i++) { 1438 cursor.moveToNext(); 1439 assertEquals(suggestions[i], cursor.getLong(0)); 1440 } 1441 } finally { 1442 TestUtils.dumpCursor(cursor); 1443 } 1444 1445 cursor.close(); 1446 } 1447 1448 private void assertDisplayNameEquals(long contactId, long rawContactId) { 1449 1450 String contactDisplayName = queryDisplayName(contactId); 1451 1452 Cursor c = queryRawContact(rawContactId); 1453 assertTrue(c.moveToFirst()); 1454 String rawDisplayName = c.getString(c.getColumnIndex(RawContacts.DISPLAY_NAME_PRIMARY)); 1455 c.close(); 1456 1457 assertTrue(contactDisplayName != null); 1458 assertTrue(rawDisplayName != null); 1459 assertEquals(rawDisplayName, contactDisplayName); 1460 } 1461 } 1462