1/*
2 * Copyright (C) 2010 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.contacts.model;
18
19import android.content.ContentUris;
20import android.net.Uri;
21import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
22import android.provider.ContactsContract.CommonDataKinds.StructuredName;
23import android.provider.ContactsContract.Contacts;
24import android.provider.ContactsContract.Data;
25import android.provider.ContactsContract.DisplayNameSources;
26import android.provider.ContactsContract.RawContacts;
27import android.provider.ContactsContract.StatusUpdates;
28import android.test.LoaderTestCase;
29import android.test.suitebuilder.annotation.LargeTest;
30
31import com.android.contacts.model.Contact;
32import com.android.contacts.model.account.AccountType;
33import com.android.contacts.model.account.AccountWithDataSet;
34import com.android.contacts.model.account.BaseAccountType;
35import com.android.contacts.test.InjectedServices;
36import com.android.contacts.tests.mocks.ContactsMockContext;
37import com.android.contacts.tests.mocks.MockAccountTypeManager;
38import com.android.contacts.tests.mocks.MockContentProvider;
39
40/**
41 * Runs ContactLoader tests for the the contact-detail and editor view.
42 */
43@LargeTest
44public class ContactLoaderTest extends LoaderTestCase {
45    private ContactsMockContext mMockContext;
46    private MockContentProvider mContactsProvider;
47
48    @Override
49    protected void setUp() throws Exception {
50        super.setUp();
51        mMockContext = new ContactsMockContext(getContext());
52        mContactsProvider = mMockContext.getContactsProvider();
53
54        InjectedServices services = new InjectedServices();
55        AccountType accountType = new BaseAccountType() {
56            @Override
57            public boolean areContactsWritable() {
58                return false;
59            }
60        };
61        accountType.accountType = "mockAccountType";
62
63        AccountWithDataSet account =
64                new AccountWithDataSet("mockAccountName", "mockAccountType", null);
65
66        mMockContext.setMockAccountTypeManager(
67                new MockAccountTypeManager(
68                        new AccountType[] { accountType }, new AccountWithDataSet[] { account }));
69    }
70
71    @Override
72    protected void tearDown() throws Exception {
73        mMockContext = null;
74        mContactsProvider = null;
75        super.tearDown();
76    }
77
78    private Contact assertLoadContact(Uri uri) {
79        final ContactLoader loader = new ContactLoader(mMockContext, uri, true);
80        return getLoaderResultSynchronously(loader);
81    }
82
83    public void testNullUri() {
84        Contact result = assertLoadContact(null);
85        assertTrue(result.isError());
86    }
87
88    public void testEmptyUri() {
89        Contact result = assertLoadContact(Uri.EMPTY);
90        assertTrue(result.isError());
91    }
92
93    public void testInvalidUri() {
94        Contact result = assertLoadContact(Uri.parse("content://wtf"));
95        assertTrue(result.isError());
96    }
97
98    public void testLoadContactWithContactIdUri() {
99        // Use content Uris that only contain the ID
100        final long contactId = 1;
101        final long rawContactId = 11;
102        final long dataId = 21;
103
104        final String lookupKey = "aa%12%@!";
105        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
106        final Uri entityUri = Uri.withAppendedPath(baseUri, Contacts.Entity.CONTENT_DIRECTORY);
107        final Uri lookupUri = ContentUris.withAppendedId(
108                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
109                contactId);
110
111        ContactQueries queries = new ContactQueries();
112        mContactsProvider.expectTypeQuery(baseUri, Contacts.CONTENT_ITEM_TYPE);
113        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
114
115        Contact contact = assertLoadContact(baseUri);
116
117        assertEquals(contactId, contact.getId());
118        assertEquals(rawContactId, contact.getNameRawContactId());
119        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
120        assertEquals(lookupKey, contact.getLookupKey());
121        assertEquals(lookupUri, contact.getLookupUri());
122        assertEquals(1, contact.getRawContacts().size());
123        assertEquals(1, contact.getStatuses().size());
124        mContactsProvider.verify();
125    }
126
127    public void testLoadContactWithOldStyleUri() {
128        // Use content Uris that only contain the ID but use the format used in Donut
129        final long contactId = 1;
130        final long rawContactId = 11;
131        final long dataId = 21;
132
133        final String lookupKey = "aa%12%@!";
134        final Uri legacyUri = ContentUris.withAppendedId(
135                Uri.parse("content://contacts"), rawContactId);
136        final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
137        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
138        final Uri lookupUri = ContentUris.withAppendedId(
139                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
140                contactId);
141        final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
142
143        ContactQueries queries = new ContactQueries();
144        queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, contactId, lookupKey);
145        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
146
147        Contact contact = assertLoadContact(legacyUri);
148
149        assertEquals(contactId, contact.getId());
150        assertEquals(rawContactId, contact.getNameRawContactId());
151        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
152        assertEquals(lookupKey, contact.getLookupKey());
153        assertEquals(lookupUri, contact.getLookupUri());
154        assertEquals(1, contact.getRawContacts().size());
155        assertEquals(1, contact.getStatuses().size());
156        mContactsProvider.verify();
157    }
158
159    public void testLoadContactWithRawContactIdUri() {
160        // Use content Uris that only contain the ID but use the format used in Donut
161        final long contactId = 1;
162        final long rawContactId = 11;
163        final long dataId = 21;
164
165        final String lookupKey = "aa%12%@!";
166        final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
167        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
168        final Uri lookupUri = ContentUris.withAppendedId(
169                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
170                contactId);
171        final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
172
173        ContactQueries queries = new ContactQueries();
174        mContactsProvider.expectTypeQuery(rawContactUri, RawContacts.CONTENT_ITEM_TYPE);
175        queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, contactId, lookupKey);
176        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
177
178        Contact contact = assertLoadContact(rawContactUri);
179
180        assertEquals(contactId, contact.getId());
181        assertEquals(rawContactId, contact.getNameRawContactId());
182        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
183        assertEquals(lookupKey, contact.getLookupKey());
184        assertEquals(lookupUri, contact.getLookupUri());
185        assertEquals(1, contact.getRawContacts().size());
186        assertEquals(1, contact.getStatuses().size());
187        mContactsProvider.verify();
188    }
189
190    public void testLoadContactWithContactLookupUri() {
191        // Use lookup-style Uris that do not contain the Contact-ID
192
193        final long contactId = 1;
194        final long rawContactId = 11;
195        final long dataId = 21;
196
197        final String lookupKey = "aa%12%@!";
198        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
199        final Uri lookupNoIdUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
200        final Uri lookupUri = ContentUris.withAppendedId(lookupNoIdUri, contactId);
201        final Uri entityUri = Uri.withAppendedPath(lookupNoIdUri, Contacts.Entity.CONTENT_DIRECTORY);
202
203        ContactQueries queries = new ContactQueries();
204        mContactsProvider.expectTypeQuery(lookupNoIdUri, Contacts.CONTENT_ITEM_TYPE);
205        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
206
207        Contact contact = assertLoadContact(lookupNoIdUri);
208
209        assertEquals(contactId, contact.getId());
210        assertEquals(rawContactId, contact.getNameRawContactId());
211        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
212        assertEquals(lookupKey, contact.getLookupKey());
213        assertEquals(lookupUri, contact.getLookupUri());
214        assertEquals(1, contact.getRawContacts().size());
215        assertEquals(1, contact.getStatuses().size());
216        mContactsProvider.verify();
217    }
218
219    public void testLoadContactWithContactLookupAndIdUri() {
220        // Use lookup-style Uris that also contain the Contact-ID
221        final long contactId = 1;
222        final long rawContactId = 11;
223        final long dataId = 21;
224
225        final String lookupKey = "aa%12%@!";
226        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
227        final Uri lookupUri = ContentUris.withAppendedId(
228                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
229                contactId);
230        final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
231
232        ContactQueries queries = new ContactQueries();
233        mContactsProvider.expectTypeQuery(lookupUri, Contacts.CONTENT_ITEM_TYPE);
234        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
235
236        Contact contact = assertLoadContact(lookupUri);
237
238        assertEquals(contactId, contact.getId());
239        assertEquals(rawContactId, contact.getNameRawContactId());
240        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
241        assertEquals(lookupKey, contact.getLookupKey());
242        assertEquals(lookupUri, contact.getLookupUri());
243        assertEquals(1, contact.getRawContacts().size());
244        assertEquals(1, contact.getStatuses().size());
245        mContactsProvider.verify();
246    }
247
248    public void testLoadContactWithContactLookupWithIncorrectIdUri() {
249        // Use lookup-style Uris that contain incorrect Contact-ID
250        // (we want to ensure that still the correct contact is chosen)
251
252        final long contactId = 1;
253        final long wrongContactId = 2;
254        final long rawContactId = 11;
255        final long wrongRawContactId = 12;
256        final long dataId = 21;
257
258        final String lookupKey = "aa%12%@!";
259        final String wrongLookupKey = "ab%12%@!";
260        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
261        final Uri wrongBaseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, wrongContactId);
262        final Uri lookupUri = ContentUris.withAppendedId(
263                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
264                contactId);
265        final Uri lookupWithWrongIdUri = ContentUris.withAppendedId(
266                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
267                wrongContactId);
268        final Uri entityUri = Uri.withAppendedPath(lookupWithWrongIdUri,
269                Contacts.Entity.CONTENT_DIRECTORY);
270
271        ContactQueries queries = new ContactQueries();
272        mContactsProvider.expectTypeQuery(lookupWithWrongIdUri, Contacts.CONTENT_ITEM_TYPE);
273        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
274
275        Contact contact = assertLoadContact(lookupWithWrongIdUri);
276
277        assertEquals(contactId, contact.getId());
278        assertEquals(rawContactId, contact.getNameRawContactId());
279        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
280        assertEquals(lookupKey, contact.getLookupKey());
281        assertEquals(lookupUri, contact.getLookupUri());
282        assertEquals(1, contact.getRawContacts().size());
283        assertEquals(1, contact.getStatuses().size());
284
285        mContactsProvider.verify();
286    }
287
288    class ContactQueries {
289        public void fetchAllData(
290                Uri baseUri, long contactId, long rawContactId, long dataId, String encodedLookup) {
291            mContactsProvider.expectQuery(baseUri)
292                    .withProjection(new String[] {
293                        Contacts.NAME_RAW_CONTACT_ID, Contacts.DISPLAY_NAME_SOURCE,
294                        Contacts.LOOKUP_KEY, Contacts.DISPLAY_NAME,
295                        Contacts.DISPLAY_NAME_ALTERNATIVE, Contacts.PHONETIC_NAME,
296                        Contacts.PHOTO_ID, Contacts.STARRED, Contacts.CONTACT_PRESENCE,
297                        Contacts.CONTACT_STATUS, Contacts.CONTACT_STATUS_TIMESTAMP,
298                        Contacts.CONTACT_STATUS_RES_PACKAGE, Contacts.CONTACT_STATUS_LABEL,
299
300                        Contacts.Entity.CONTACT_ID,
301                        Contacts.Entity.RAW_CONTACT_ID,
302
303                        RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE,
304                        RawContacts.DATA_SET, RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
305                        RawContacts.DIRTY, RawContacts.VERSION, RawContacts.SOURCE_ID,
306                        RawContacts.SYNC1, RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4,
307                        RawContacts.DELETED, RawContacts.NAME_VERIFIED,
308
309                        Contacts.Entity.DATA_ID,
310
311                        Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5,
312                        Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10,
313                        Data.DATA11, Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15,
314                        Data.SYNC1, Data.SYNC2, Data.SYNC3, Data.SYNC4,
315                        Data.DATA_VERSION, Data.IS_PRIMARY,
316                        Data.IS_SUPER_PRIMARY, Data.MIMETYPE, Data.RES_PACKAGE,
317
318                        GroupMembership.GROUP_SOURCE_ID,
319
320                        Data.PRESENCE, Data.CHAT_CAPABILITY,
321                        Data.STATUS, Data.STATUS_RES_PACKAGE, Data.STATUS_ICON,
322                        Data.STATUS_LABEL, Data.STATUS_TIMESTAMP,
323
324                        Contacts.PHOTO_URI,
325
326                        Contacts.SEND_TO_VOICEMAIL,
327                        Contacts.CUSTOM_RINGTONE,
328                        Contacts.IS_USER_PROFILE,
329                    })
330                    .withSortOrder(Contacts.Entity.RAW_CONTACT_ID)
331                    .returnRow(
332                        rawContactId, 40,
333                        "aa%12%@!", "John Doe", "Doe, John", "jdo",
334                        0, 0, StatusUpdates.AVAILABLE,
335                        "Having lunch", 0,
336                        "mockPkg1", 10,
337
338                        contactId,
339                        rawContactId,
340
341                        "mockAccountName", "mockAccountType", null, "mockAccountType",
342                        0, 1, 0,
343                        "sync1", "sync2", "sync3", "sync4",
344                        0, 0,
345
346                        dataId,
347
348                        "dat1", "dat2", "dat3", "dat4", "dat5",
349                        "dat6", "dat7", "dat8", "dat9", "dat10",
350                        "dat11", "dat12", "dat13", "dat14", "dat15",
351                        "syn1", "syn2", "syn3", "syn4",
352
353                        0, 0,
354                        0, StructuredName.CONTENT_ITEM_TYPE, "mockPkg2",
355
356                        "groupId",
357
358                        StatusUpdates.INVISIBLE, null,
359                        "Having dinner", "mockPkg3", 0,
360                        20, 0,
361
362                        "content:some.photo.uri",
363
364                        0,
365                        null,
366                        0
367                    );
368        }
369
370        void fetchLookupAndId(final Uri sourceUri, final long expectedContactId,
371                final String expectedEncodedLookup) {
372            mContactsProvider.expectQuery(sourceUri)
373                    .withProjection(Contacts.LOOKUP_KEY, Contacts._ID)
374                    .returnRow(expectedEncodedLookup, expectedContactId);
375        }
376
377        void fetchContactIdAndLookupFromRawContactUri(final Uri rawContactUri,
378                final long expectedContactId, final String expectedEncodedLookup) {
379            // TODO: use a lighter query by joining rawcontacts with contacts in provider
380            // (See ContactContracts.java)
381            final Uri dataUri = Uri.withAppendedPath(rawContactUri,
382                    RawContacts.Data.CONTENT_DIRECTORY);
383            mContactsProvider.expectQuery(dataUri)
384                    .withProjection(RawContacts.CONTACT_ID, Contacts.LOOKUP_KEY)
385                    .returnRow(expectedContactId, expectedEncodedLookup);
386        }
387    }
388}
389