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