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