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