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