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;
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
57public 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@android.com");
342
343        long rawContactId2 = RawContactUtil.createRawContact(mResolver, ACCOUNT_2);
344        insertEmail(rawContactId2, "lightning@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@android.com");
352
353        long rawContactId2 = RawContactUtil.createRawContact(mResolver, ACCOUNT_2);
354        DataUtil.insertStructuredName(mResolver, rawContactId2, "Lightning", "McQueen");
355        insertEmail(rawContactId2, "mcqueen@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@android.com");
364
365        long rawContactId2 = RawContactUtil.createRawContact(mResolver, ACCOUNT_2);
366        insertEmail(rawContactId2, "doc@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@android.com");
375
376        long rawContactId2 = RawContactUtil.createRawContact(mResolver, ACCOUNT_2);
377        DataUtil.insertStructuredName(mResolver, rawContactId2, "Luigi", "Guido");
378        insertEmail(rawContactId2, "hicky@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@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@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@android.com");
736
737        long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe",
738                ACCOUNT_1);
739        insertEmail(rawContactId2, "lightning@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@android.com");
748
749        long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe",
750                ACCOUNT_1);
751        insertEmail(rawContactId2, "lightning2@android.com");
752        insertEmail(rawContactId2, "lightning3@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@android.com");
868
869        long rawContactId2 = RawContactUtil.createRawContact(mResolver);
870        DataUtil.insertStructuredName(mResolver, rawContactId2, "Charles", "Muntz");
871        insertEmail(rawContactId2, "up@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@android.com");
886
887        long rawContactId2 = RawContactUtil.createRawContact(mResolver);
888        DataUtil.insertStructuredName(mResolver, rawContactId2, "Lucius", "Best");
889        insertEmail(rawContactId2, "incrediball@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@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@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@android.com");
1166        assertEquals("eclair@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