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