ContactsProvider2Test.java revision f1efadb1255fd75305b59802f736905b9d66e449
1d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov/*
2d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * Copyright (C) 2009 The Android Open Source Project
3d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov *
4d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License");
5d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * you may not use this file except in compliance with the License.
6d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * You may obtain a copy of the License at
7d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov *
8d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov *      http://www.apache.org/licenses/LICENSE-2.0
9d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov *
10d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software
11d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS,
12d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * See the License for the specific language governing permissions and
14d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * limitations under the License.
15d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov */
168920a04b4a68ed6b548bcdef5ca8736dcf8b69b1Omari Stephens
1728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarpackage com.android.providers.contacts;
18d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
19d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport com.android.common.contacts.DataUsageStatUpdater;
20b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.internal.util.ArrayUtils;
21d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
22d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
2370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wongimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
24d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikovimport com.android.providers.contacts.tests.R;
25d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikovimport com.google.android.collect.Lists;
26d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
27d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.accounts.Account;
289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.content.ContentProviderOperation;
2933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikovimport android.content.ContentProviderResult;
30d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.content.ContentUris;
31d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.content.ContentValues;
3233b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikovimport android.content.Entity;
33c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.content.EntityIterator;
341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikovimport android.content.res.AssetFileDescriptor;
35d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.database.Cursor;
36ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikovimport android.database.sqlite.SQLiteDatabase;
37c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikovimport android.net.Uri;
389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.provider.ContactsContract;
395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
403cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email;
414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Im;
4309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Organization;
4433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
4562318e1ea8306142a10526534b7d83560ecf5b3aFred Quintanaimport android.provider.ContactsContract.CommonDataKinds.Photo;
46916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
4789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
4882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikovimport android.provider.ContactsContract.ContactCounts;
499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.provider.ContactsContract.Contacts;
509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.provider.ContactsContract.Data;
51bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millarimport android.provider.ContactsContract.DataUsageFeedback;
52916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikovimport android.provider.ContactsContract.Directory;
53a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaokaimport android.provider.ContactsContract.DisplayNameSources;
549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.provider.ContactsContract.DisplayPhoto;
558e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikovimport android.provider.ContactsContract.FullNameStyle;
564097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.Groups;
574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikovimport android.provider.ContactsContract.PhoneLookup;
587d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekhimport android.provider.ContactsContract.PhoneticNameStyle;
59d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.provider.ContactsContract.Profile;
60d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.provider.ContactsContract.ProviderStatus;
615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.RawContactsEntity;
635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.SearchSnippetColumns;
645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikovimport android.provider.ContactsContract.Settings;
658920a04b4a68ed6b548bcdef5ca8736dcf8b69b1Omari Stephensimport android.provider.ContactsContract.StatusUpdates;
66d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.provider.ContactsContract.StreamItemPhotos;
67d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.provider.ContactsContract.StreamItems;
68d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.provider.LiveFolders;
69d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.provider.OpenableColumns;
70d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.test.MoreAsserts;
718920a04b4a68ed6b548bcdef5ca8736dcf8b69b1Omari Stephensimport android.test.suitebuilder.annotation.LargeTest;
7228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport android.text.TextUtils;
73d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
74d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport java.io.FileInputStream;
75d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport java.io.IOException;
76d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport java.io.InputStream;
77d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport java.io.OutputStream;
783cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikovimport java.text.Collator;
793cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikovimport java.util.ArrayList;
803cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikovimport java.util.Arrays;
813cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikovimport java.util.List;
823cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikovimport java.util.Locale;
833cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov
843cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov/**
853cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov * Unit tests for {@link ContactsProvider2}.
863cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov *
873cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov * Run the test like this:
883cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov * <code>
893cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov * adb shell am instrument -e class com.android.providers.contacts.ContactsProvider2Test -w \
903cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov *         com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
913cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov * </code>
923cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov */
933cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov@LargeTest
943cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikovpublic class ContactsProvider2Test extends BaseContactsProvider2Test {
953cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov
963cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov    private static final Account ACCOUNT_1 = new Account("account_name_1", "account_type_1");
973cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov    private static final Account ACCOUNT_2 = new Account("account_name_2", "account_type_2");
984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
993cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov    public void testContactsProjection() {
1003cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        assertProjection(Contacts.CONTENT_URI, new String[]{
1014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts._ID,
10281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                Contacts.DISPLAY_NAME_PRIMARY,
1033cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE,
1043cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Contacts.DISPLAY_NAME_SOURCE,
1053cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Contacts.PHONETIC_NAME,
1064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHONETIC_NAME_STYLE,
1074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.SORT_KEY_PRIMARY,
1084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.SORT_KEY_ALTERNATIVE,
1094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.LAST_TIME_CONTACTED,
1104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.TIMES_CONTACTED,
1114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.STARRED,
1124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.IN_VISIBLE_GROUP,
1134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHOTO_ID,
1144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHOTO_FILE_ID,
1154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHOTO_URI,
1164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHOTO_THUMBNAIL_URI,
1174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CUSTOM_RINGTONE,
1184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.HAS_PHONE_NUMBER,
1194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.SEND_TO_VOICEMAIL,
1204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.IS_USER_PROFILE,
1214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.LOOKUP_KEY,
1224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.NAME_RAW_CONTACT_ID,
1234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_PRESENCE,
1244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_CHAT_CAPABILITY,
1254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS,
1264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP,
1274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
1284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
12981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
1304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        });
1313cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov    }
13289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov
13389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov    public void testContactsWithSnippetProjection() {
13489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        assertProjection(Contacts.CONTENT_FILTER_URI.buildUpon().appendPath("nothing").build(),
13589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            new String[]{
13689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Contacts._ID,
13789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Contacts.DISPLAY_NAME_PRIMARY,
13889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE,
13989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Contacts.DISPLAY_NAME_SOURCE,
14089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Contacts.PHONETIC_NAME,
14189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Contacts.PHONETIC_NAME_STYLE,
14289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Contacts.SORT_KEY_PRIMARY,
14389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Contacts.SORT_KEY_ALTERNATIVE,
14489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Contacts.LAST_TIME_CONTACTED,
14589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Contacts.TIMES_CONTACTED,
1464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.STARRED,
1477d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh                Contacts.IN_VISIBLE_GROUP,
1483cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Contacts.PHOTO_ID,
1494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHOTO_FILE_ID,
1504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHOTO_URI,
1514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHOTO_THUMBNAIL_URI,
1524a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CUSTOM_RINGTONE,
1534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.HAS_PHONE_NUMBER,
1544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.SEND_TO_VOICEMAIL,
1554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.IS_USER_PROFILE,
1564a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.LOOKUP_KEY,
1574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.NAME_RAW_CONTACT_ID,
1584a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_PRESENCE,
1594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_CHAT_CAPABILITY,
1604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS,
1614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP,
1624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
1634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
1644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
1654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
1663cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                SearchSnippetColumns.SNIPPET,
1674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        });
1684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
1694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
1704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    public void testRawContactsProjection() {
1714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        assertProjection(RawContacts.CONTENT_URI, new String[]{
1724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts._ID,
1734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.CONTACT_ID,
1744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
1754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
1764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SOURCE_ID,
1774a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.VERSION,
1784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.RAW_CONTACT_IS_USER_PROFILE,
17948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                RawContacts.DIRTY,
1804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.DELETED,
1814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.DISPLAY_NAME_PRIMARY,
1824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.DISPLAY_NAME_ALTERNATIVE,
1834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.DISPLAY_NAME_SOURCE,
1845e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                RawContacts.PHONETIC_NAME,
1855e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                RawContacts.PHONETIC_NAME_STYLE,
1865e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                RawContacts.NAME_VERIFIED,
1875e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                RawContacts.SORT_KEY_PRIMARY,
1885e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                RawContacts.SORT_KEY_ALTERNATIVE,
1895e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                RawContacts.TIMES_CONTACTED,
1905e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                RawContacts.LAST_TIME_CONTACTED,
1914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.CUSTOM_RINGTONE,
1924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SEND_TO_VOICEMAIL,
1934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.STARRED,
1944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.AGGREGATION_MODE,
1955e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                RawContacts.SYNC1,
1964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SYNC2,
1974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SYNC3,
1985e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                RawContacts.SYNC4,
1994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        });
2005e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
2015e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
2025e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    public void testDataProjection() {
2035e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        assertProjection(Data.CONTENT_URI, new String[]{
2045e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data._ID,
2055e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.RAW_CONTACT_ID,
2065e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.DATA_VERSION,
2075e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.IS_PRIMARY,
2084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.IS_SUPER_PRIMARY,
2094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.RES_PACKAGE,
210e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.MIMETYPE,
211e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA1,
212e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA2,
213e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA3,
214e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA4,
215e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA5,
216e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA6,
217e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA7,
218e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA8,
2194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA9,
2204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA10,
2214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA11,
222e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA12,
223e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA13,
224e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA14,
225e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.DATA15,
226e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.SYNC1,
227e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.SYNC2,
228e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.SYNC3,
229e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.SYNC4,
230e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                Data.CONTACT_ID,
2314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.PRESENCE,
2324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.CHAT_CAPABILITY,
2334e3790e646bce315a1b34c9dc474eb8152c8eea2Daisuke Miyakawa                Data.STATUS,
2344e3790e646bce315a1b34c9dc474eb8152c8eea2Daisuke Miyakawa                Data.STATUS_TIMESTAMP,
2354e3790e646bce315a1b34c9dc474eb8152c8eea2Daisuke Miyakawa                Data.STATUS_RES_PACKAGE,
2364e3790e646bce315a1b34c9dc474eb8152c8eea2Daisuke Miyakawa                Data.STATUS_LABEL,
2374e3790e646bce315a1b34c9dc474eb8152c8eea2Daisuke Miyakawa                Data.STATUS_ICON,
2384e3790e646bce315a1b34c9dc474eb8152c8eea2Daisuke Miyakawa                RawContacts.ACCOUNT_NAME,
2394e3790e646bce315a1b34c9dc474eb8152c8eea2Daisuke Miyakawa                RawContacts.ACCOUNT_TYPE,
2404e3790e646bce315a1b34c9dc474eb8152c8eea2Daisuke Miyakawa                RawContacts.SOURCE_ID,
241e3eb7ef438010c893c429f3031dcc7298171865dDmitri Plotnikov                RawContacts.VERSION,
2424e3790e646bce315a1b34c9dc474eb8152c8eea2Daisuke Miyakawa                RawContacts.DIRTY,
2434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.NAME_VERIFIED,
2444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.RAW_CONTACT_IS_USER_PROFILE,
245653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts._ID,
246653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.DISPLAY_NAME_PRIMARY,
247653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE,
248653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.DISPLAY_NAME_SOURCE,
249653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.PHONETIC_NAME,
250653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.PHONETIC_NAME_STYLE,
251653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.SORT_KEY_PRIMARY,
252653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.SORT_KEY_ALTERNATIVE,
253653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.LAST_TIME_CONTACTED,
2545870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Contacts.TIMES_CONTACTED,
255653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.STARRED,
256653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.IN_VISIBLE_GROUP,
257653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.PHOTO_ID,
258653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.PHOTO_FILE_ID,
259653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.PHOTO_URI,
260653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.PHOTO_THUMBNAIL_URI,
2615870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                Contacts.CUSTOM_RINGTONE,
262653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.SEND_TO_VOICEMAIL,
263653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.LOOKUP_KEY,
264653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.NAME_RAW_CONTACT_ID,
265653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.HAS_PHONE_NUMBER,
266653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.CONTACT_PRESENCE,
267653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.CONTACT_CHAT_CAPABILITY,
268653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.CONTACT_STATUS,
269653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP,
270653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
271653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
272653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
273653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID,
2745870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        });
27581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
276653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov
277653f73c9417ee0d2cf90e9aacd32848016747cf7Dmitri Plotnikov    public void testDistinctDataProjection() {
2784a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        assertProjection(Phone.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(),
2794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            new String[]{
2804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data._ID,
2814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA_VERSION,
2824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.IS_PRIMARY,
2834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.IS_SUPER_PRIMARY,
2844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.RES_PACKAGE,
2854a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.MIMETYPE,
2864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA1,
2874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA2,
2884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA3,
2894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA4,
2904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA5,
2914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA6,
2924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA7,
2934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA8,
2944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA9,
2954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA10,
2964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA11,
2974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA12,
2984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA13,
2994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA14,
3004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA15,
3014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC1,
3024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC2,
3034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC3,
3044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC4,
3054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.CONTACT_ID,
3064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.PRESENCE,
3074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.CHAT_CAPABILITY,
3084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.STATUS,
30948828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                Data.STATUS_TIMESTAMP,
3104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.STATUS_RES_PACKAGE,
3114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.STATUS_LABEL,
3124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.STATUS_ICON,
3135e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                RawContacts.RAW_CONTACT_IS_USER_PROFILE,
3144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts._ID,
3154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.DISPLAY_NAME_PRIMARY,
3164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE,
3175e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.DISPLAY_NAME_SOURCE,
3184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHONETIC_NAME,
3194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHONETIC_NAME_STYLE,
3204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.SORT_KEY_PRIMARY,
3214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.SORT_KEY_ALTERNATIVE,
3224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.LAST_TIME_CONTACTED,
3234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.TIMES_CONTACTED,
3244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.STARRED,
3254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.IN_VISIBLE_GROUP,
32608768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                Contacts.PHOTO_ID,
32708768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                Contacts.PHOTO_FILE_ID,
32808768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                Contacts.PHOTO_URI,
32908768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                Contacts.PHOTO_THUMBNAIL_URI,
33008768a0f3434130fa46379c1bbfec93a19094939Dmitri Plotnikov                Contacts.HAS_PHONE_NUMBER,
3314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CUSTOM_RINGTONE,
3324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.SEND_TO_VOICEMAIL,
3335e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.LOOKUP_KEY,
3345e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.CONTACT_PRESENCE,
3355e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.CONTACT_CHAT_CAPABILITY,
3365e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.CONTACT_STATUS,
3375e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP,
3385e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
3395e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
3405e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
3415e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID,
3425e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        });
3435e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    }
3445e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov
3455e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov    public void testEntityProjection() {
3465e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov        assertProjection(
3475e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, 0),
3485e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                    Contacts.Entity.CONTENT_DIRECTORY),
3495e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov            new String[]{
3505e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.Entity._ID,
3515e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.Entity.DATA_ID,
3525e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Contacts.Entity.RAW_CONTACT_ID,
3535e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.DATA_VERSION,
3545e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.IS_PRIMARY,
3555e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.IS_SUPER_PRIMARY,
3561e530df9f7e496dc47f77d4323c89bd413b79b64Dmitri Plotnikov                Data.RES_PACKAGE,
3575e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.MIMETYPE,
3585e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.DATA1,
3595e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.DATA2,
3605e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.DATA3,
3615e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.DATA4,
3625e99505757457d11d9388f6d04960e97fc776a59Dmitri Plotnikov                Data.DATA5,
3634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA6,
3644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA7,
3654a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA8,
3664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA9,
3674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA10,
3684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA11,
3694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA12,
3704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA13,
3714a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA14,
3724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA15,
3734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC1,
3744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC2,
3754a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC3,
3764a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC4,
37748828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                Data.CONTACT_ID,
37848828f54daafda2edb122258c4c6a7d2ca704128Dmitri Plotnikov                Data.PRESENCE,
3794a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.CHAT_CAPABILITY,
3804a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.STATUS,
3814a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.STATUS_TIMESTAMP,
3824a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.STATUS_RES_PACKAGE,
3834a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.STATUS_LABEL,
3844a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.STATUS_ICON,
38582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                RawContacts.ACCOUNT_NAME,
3864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
3874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SOURCE_ID,
3884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.VERSION,
3894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.DELETED,
3904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.DIRTY,
3914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.NAME_VERIFIED,
3920a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                RawContacts.SYNC1,
3934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SYNC2,
3944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SYNC3,
39582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                RawContacts.SYNC4,
39682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts._ID,
397ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Contacts.DISPLAY_NAME_PRIMARY,
398ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Contacts.DISPLAY_NAME_ALTERNATIVE,
399ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Contacts.DISPLAY_NAME_SOURCE,
4004a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHONETIC_NAME,
4014a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHONETIC_NAME_STYLE,
402ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Contacts.SORT_KEY_PRIMARY,
4034a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.SORT_KEY_ALTERNATIVE,
4044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.LAST_TIME_CONTACTED,
40582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.TIMES_CONTACTED,
40682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.STARRED,
407ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Contacts.IN_VISIBLE_GROUP,
408ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Contacts.PHOTO_ID,
4094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHOTO_FILE_ID,
410ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Contacts.PHOTO_URI,
4114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.PHOTO_THUMBNAIL_URI,
4124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CUSTOM_RINGTONE,
4134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.SEND_TO_VOICEMAIL,
414ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Contacts.IS_USER_PROFILE,
4154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.LOOKUP_KEY,
4164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.NAME_RAW_CONTACT_ID,
41782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.HAS_PHONE_NUMBER,
4184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_PRESENCE,
4194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_CHAT_CAPABILITY,
42082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS,
4214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS_TIMESTAMP,
4224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS_RES_PACKAGE,
42382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Contacts.CONTACT_STATUS_LABEL,
4244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Contacts.CONTACT_STATUS_ICON,
4254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID,
42682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        });
4274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
428ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov
4294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    public void testRawEntityProjection() {
4304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        assertProjection(RawContactsEntity.CONTENT_URI, new String[]{
4314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.Entity.DATA_ID,
4324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts._ID,
4334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.CONTACT_ID,
4344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.ACCOUNT_NAME,
4354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.ACCOUNT_TYPE,
4364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SOURCE_ID,
4374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.VERSION,
4384a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.DIRTY,
439ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                RawContacts.NAME_VERIFIED,
4404a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.DELETED,
4414a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SYNC1,
4424a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SYNC2,
4434a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SYNC3,
4444a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.SYNC4,
4454a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.STARRED,
4464a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                RawContacts.RAW_CONTACT_IS_USER_PROFILE,
447ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Data.DATA_VERSION,
4484a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.IS_PRIMARY,
4494a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.IS_SUPER_PRIMARY,
4504a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.RES_PACKAGE,
4514a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.MIMETYPE,
45282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.DATA1,
4534a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA2,
4544a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA3,
4554a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA4,
45682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Data.DATA5,
4574a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA6,
458ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Data.DATA7,
4594a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA8,
4604a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA9,
4614a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA10,
4624a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA11,
4634a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA12,
4644a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA13,
465ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                Data.DATA14,
4664a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.DATA15,
4674a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC1,
4684a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC2,
4694a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC3,
4704a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                Data.SYNC4,
471ff2de103f7e3eeeff4665ef63f07460fef053d6dDmitri Plotnikov                GroupMembership.GROUP_SOURCE_ID,
4724a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        });
4734a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
4744a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
4753cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov    public void testPhoneLookupProjection() {
4763cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        assertProjection(PhoneLookup.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(),
4770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov            new String[]{
47819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup._ID,
47919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.LOOKUP_KEY,
48019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.DISPLAY_NAME,
48119a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.LAST_TIME_CONTACTED,
48219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.TIMES_CONTACTED,
48319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.STARRED,
48419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.IN_VISIBLE_GROUP,
48519a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.PHOTO_ID,
48619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.PHOTO_URI,
48719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.PHOTO_THUMBNAIL_URI,
48819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.CUSTOM_RINGTONE,
48919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.HAS_PHONE_NUMBER,
49019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                PhoneLookup.SEND_TO_VOICEMAIL,
49182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                PhoneLookup.NUMBER,
49282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                PhoneLookup.TYPE,
49382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                PhoneLookup.LABEL,
49482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                PhoneLookup.NORMALIZED_NUMBER,
49519a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov        });
49619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
49782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
49819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    public void testGroupsProjection() {
499a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov        assertProjection(Groups.CONTENT_URI, new String[]{
500a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                Groups._ID,
50119a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.ACCOUNT_NAME,
50219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.ACCOUNT_TYPE,
50319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.SOURCE_ID,
50419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.DIRTY,
50519a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.VERSION,
50682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Groups.RES_PACKAGE,
5070a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                Groups.TITLE,
50819a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.TITLE_RES,
50919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.GROUP_VISIBLE,
51019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.SYSTEM_ID,
511a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                Groups.DELETED,
51219a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.NOTES,
51319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.ACTION,
51419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.ACTION_URI,
51519a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.SHOULD_SYNC,
51619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.FAVORITES,
51782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                Groups.AUTO_ADD,
5180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                Groups.GROUP_IS_READ_ONLY,
51919a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.SYNC1,
52019a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.SYNC2,
52119a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov                Groups.SYNC3,
522a6def2055f5d12cb6ee5cc3dc1adaf39f2b7c97cDmitri Plotnikov                Groups.SYNC4,
52319a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov        });
52419a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    }
52519a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov
52619a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov    public void testGroupsSummaryProjection() {
52719a0962e62c13a5e5f8e5b4eed5e30d3477894b4Dmitri Plotnikov        assertProjection(Groups.CONTENT_SUMMARY_URI, new String[]{
52889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups._ID,
52989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.ACCOUNT_NAME,
53089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.ACCOUNT_TYPE,
53189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.SOURCE_ID,
53289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.DIRTY,
53389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.VERSION,
53489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.RES_PACKAGE,
53589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.TITLE,
53689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.TITLE_RES,
53789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.GROUP_VISIBLE,
53889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.SYSTEM_ID,
53989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.DELETED,
54089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Groups.NOTES,
5413cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.ACTION,
5423cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.ACTION_URI,
5433cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.SHOULD_SYNC,
5443cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.FAVORITES,
5453cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.AUTO_ADD,
5463cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.GROUP_IS_READ_ONLY,
5473cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.SYNC1,
5483cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.SYNC2,
5493cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.SYNC3,
5503cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.SYNC4,
5513cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.SUMMARY_COUNT,
5523cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                Groups.SUMMARY_WITH_PHONES,
5533cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        });
55494021b213e4db367f60b30fcbfe9019e28571784Fred Quintana    }
5553cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov
5563cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov    public void testAggregateExceptionProjection() {
5573cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        assertProjection(AggregationExceptions.CONTENT_URI, new String[]{
5583cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                AggregationExceptionColumns._ID,
5593cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                AggregationExceptions.TYPE,
5603cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                AggregationExceptions.RAW_CONTACT_ID1,
5613cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                AggregationExceptions.RAW_CONTACT_ID2,
56273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        });
5633cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov    }
5643cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov
5653cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov    public void testSettingsProjection() {
56689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        assertProjection(Settings.CONTENT_URI, new String[]{
56789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Settings.ACCOUNT_NAME,
56889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Settings.ACCOUNT_TYPE,
56989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Settings.UNGROUPED_VISIBLE,
57089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Settings.SHOULD_SYNC,
57189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Settings.ANY_UNSYNCED,
57289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Settings.UNGROUPED_COUNT,
57389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                Settings.UNGROUPED_WITH_PHONES,
57489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        });
57589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov    }
57689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov
57789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov    public void testStatusUpdatesProjection() {
57889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        assertProjection(StatusUpdates.CONTENT_URI, new String[]{
57989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                PresenceColumns.RAW_CONTACT_ID,
58089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                StatusUpdates.DATA_ID,
5814097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                StatusUpdates.IM_ACCOUNT,
582d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                StatusUpdates.IM_HANDLE,
5834097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                StatusUpdates.PROTOCOL,
5844097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                StatusUpdates.CUSTOM_PROTOCOL,
5855ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                StatusUpdates.PRESENCE,
5864097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                StatusUpdates.CHAT_CAPABILITY,
587635a11c53a532d9b5aba5fd7c51a8d47dcb0aaf2Dmitri Plotnikov                StatusUpdates.STATUS,
5884097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                StatusUpdates.STATUS_TIMESTAMP,
5894097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                StatusUpdates.STATUS_RES_PACKAGE,
59067c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                StatusUpdates.STATUS_ICON,
59167c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                StatusUpdates.STATUS_LABEL,
59267c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        });
59367c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    }
59467c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov
59567c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov    public void testLiveFoldersProjection() {
59667c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov        assertProjection(
597635a11c53a532d9b5aba5fd7c51a8d47dcb0aaf2Dmitri Plotnikov            Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "live_folders/contacts"),
59867c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov            new String[]{
59967c9ed1cefa5c084d3f373d7f1ecb7122983ff15Dmitri Plotnikov                LiveFolders._ID,
6004097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                LiveFolders.NAME,
601d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        });
6024097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    }
6034097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
6044097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    public void testDirectoryProjection() {
6055ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        assertProjection(Directory.CONTENT_URI, new String[]{
6064097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                Directory._ID,
6075ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                Directory.PACKAGE_NAME,
6084097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                Directory.TYPE_RESOURCE_ID,
6094097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                Directory.DISPLAY_NAME,
6105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Directory.DIRECTORY_AUTHORITY,
6115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Directory.ACCOUNT_TYPE,
6125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Directory.ACCOUNT_NAME,
6135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Directory.EXPORT_SUPPORT,
6145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Directory.SHORTCUT_SUPPORT,
6155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Directory.PHOTO_SUPPORT,
6165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        });
6175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
6185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    public void testRawContactsInsert() {
6205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        ContentValues values = new ContentValues();
6215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.ACCOUNT_NAME, "a");
6235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.ACCOUNT_TYPE, "b");
6245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.SOURCE_ID, "c");
6255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.VERSION, 42);
6265dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.DIRTY, 1);
6275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.DELETED, 1);
6285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
6295dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.CUSTOM_RINGTONE, "d");
6305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
6315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
6325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.STARRED, 1);
6335dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.SYNC1, "e");
6345dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.SYNC2, "f");
6355dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.SYNC3, "g");
6365dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.SYNC4, "h");
6375dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6385dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri rowUri = mResolver.insert(RawContacts.CONTENT_URI, values);
6395dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long rawContactId = ContentUris.parseId(rowUri);
6405dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6415dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertStoredValues(rowUri, values);
6425dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertSelection(RawContacts.CONTENT_URI, values, RawContacts._ID, rawContactId);
6435dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertNetworkNotified(true);
6445dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
6455dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    public void testDataDirectoryWithLookupUri() {
6475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        ContentValues values = new ContentValues();
6485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long rawContactId = createRawContactWithName();
6505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        insertPhoneNumber(rawContactId, "555-GOOG-411");
6515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        insertEmail(rawContactId, "google@android.com");
6525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long contactId = queryContactId(rawContactId);
6545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String lookupKey = queryLookupKey(contactId);
6555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        // Complete and valid lookup URI
6575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
6585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
6595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertDataRows(dataUri, values);
6615dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6625dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        // Complete but stale lookup URI
6635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        lookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
6645dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
6655dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertDataRows(dataUri, values);
6665dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        // Incomplete lookup URI (lookup key only, no contact ID)
6685dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        dataUri = Uri.withAppendedPath(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
6694cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao                lookupKey), Contacts.Data.CONTENT_DIRECTORY);
6704cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        assertDataRows(dataUri, values);
6715dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
6725dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6735dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    private void assertDataRows(Uri dataUri, ContentValues values) {
6745dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Cursor cursor = mResolver.query(dataUri, new String[]{ Data.DATA1 }, null, null, Data._ID);
6755dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertEquals(3, cursor.getCount());
6765dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        cursor.moveToFirst();
6775dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Data.DATA1, "John Doe");
6785dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertCursorValues(cursor, values);
6795dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        cursor.moveToNext();
6814cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        values.put(Data.DATA1, "555-GOOG-411");
6824cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        assertCursorValues(cursor, values);
6835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        cursor.moveToNext();
6855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Data.DATA1, "google@android.com");
6865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertCursorValues(cursor, values);
6875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        cursor.close();
6895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
6905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    public void testContactEntitiesWithIdBasedUri() {
6925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        ContentValues values = new ContentValues();
6935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Account account1 = new Account("act1", "actype1");
6945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Account account2 = new Account("act2", "actype2");
6955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
6965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long rawContactId1 = createRawContactWithName(account1);
6975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
6985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90,
6995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_CAMERA);
7005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
7015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long rawContactId2 = createRawContact(account2);
7025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        setAggregationException(
7035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
7045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
7055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long contactId = queryContactId(rawContactId1);
7065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
7075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
7095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
7105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
7115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
7125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
7135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    public void testContactEntitiesWithLookupUri() {
7145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        ContentValues values = new ContentValues();
7155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Account account1 = new Account("act1", "actype1");
7165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Account account2 = new Account("act2", "actype2");
7175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
7185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long rawContactId1 = createRawContactWithName(account1);
7195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
7205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90,
7215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_CAMERA);
7225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
7235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long rawContactId2 = createRawContact(account2);
7245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        setAggregationException(
7255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
7265dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
7275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long contactId = queryContactId(rawContactId1);
7285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        String lookupKey = queryLookupKey(contactId);
72925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov
73025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        // First try with a matching contact ID
73125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        Uri contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
73225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        Uri entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
73325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
73425abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov
73525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        // Now try with a contact ID mismatch
7360c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
7370c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
73825abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
73925abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov
74025abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        // Now try without an ID altogether
74125abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        contactLookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
74225abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
74325abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov        assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
74481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    }
74525abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov
74625abcf949c0dd826a770b437489b83de48975ceaDmitri Plotnikov    private void assertEntityRows(Uri entityUri, long contactId, long rawContactId1,
74701911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov            long rawContactId2) {
74801911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        ContentValues values = new ContentValues();
74901911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov
750a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        Cursor cursor = mResolver.query(entityUri, null, null, null,
75101911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov                Contacts.Entity.RAW_CONTACT_ID + "," + Contacts.Entity.DATA_ID);
75201911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        assertEquals(3, cursor.getCount());
75301911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov
75401911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        // First row - name
75501911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        cursor.moveToFirst();
75601911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        values.put(Contacts.Entity.CONTACT_ID, contactId);
75701911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1);
75801911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        values.put(Contacts.Entity.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
75901911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        values.put(Contacts.Entity.DATA1, "John Doe");
76001911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        values.put(Contacts.Entity.ACCOUNT_NAME, "act1");
76101911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1");
76201911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
76301911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
764a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
765a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
766a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
7675dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
76801911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        values.putNull(Contacts.Entity.PRESENCE);
76901911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        assertCursorValues(cursor, values);
770a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka
771a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        // Second row - IM
772a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        cursor.moveToNext();
773a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        values.put(Contacts.Entity.CONTACT_ID, contactId);
774a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1);
775a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        values.put(Contacts.Entity.MIMETYPE, Im.CONTENT_ITEM_TYPE);
776a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        values.put(Contacts.Entity.DATA1, "gtalk");
777a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        values.put(Contacts.Entity.ACCOUNT_NAME, "act1");
778a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1");
779a5d05d90333a70d471d78e82caeb5cfa2e4d4c59Tadashi G. Takaoka        values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
7805dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
7815dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
7825dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
7835dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
7845dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
7855dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.PRESENCE, StatusUpdates.IDLE);
7865dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertCursorValues(cursor, values);
7875dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
7885dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        // Third row - second raw contact, not data
7895dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        cursor.moveToNext();
7905dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.CONTACT_ID, contactId);
7915dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId2);
7925dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.putNull(Contacts.Entity.MIMETYPE);
7935dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.putNull(Contacts.Entity.DATA_ID);
7945dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.putNull(Contacts.Entity.DATA1);
7955dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.ACCOUNT_NAME, "act2");
7965dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.ACCOUNT_TYPE, "actype2");
7975dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
7985dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
7995dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
8005dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
8015dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
8025dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
8035dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.putNull(Contacts.Entity.PRESENCE);
8045dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertCursorValues(cursor, values);
8055dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
8065dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        cursor.close();
8075dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
8085dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
8095dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    public void testDataInsert() {
8105dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long rawContactId = createRawContactWithName("John", "Doe");
8115dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
8125dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        ContentValues values = new ContentValues();
8135dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        putDataValues(values, rawContactId);
8145dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
8155dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long dataId = ContentUris.parseId(dataUri);
8165dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
8175dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long contactId = queryContactId(rawContactId);
8185dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.CONTACT_ID, contactId);
8195dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertStoredValues(dataUri, values);
8205dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
8215dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertSelection(Data.CONTENT_URI, values, Data._ID, dataId);
8225dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
8235dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        // Access the same data through the directory under RawContacts
8245dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
8255dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri rawContactDataUri =
8265dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov                Uri.withAppendedPath(rawContactUri, RawContacts.Data.CONTENT_DIRECTORY);
8275dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertSelection(rawContactDataUri, values, Data._ID, dataId);
8285dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
8295dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        // Access the same data through the directory under Contacts
8305dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
8315dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri contactDataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY);
8325dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertSelection(contactDataUri, values, Data._ID, dataId);
8330b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov        assertNetworkNotified(true);
8340b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov    }
8350b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov
8360b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov    public void testRawContactDataQuery() {
8370b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov        Account account1 = new Account("a", "b");
8380b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov        Account account2 = new Account("c", "d");
8390b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov        long rawContactId1 = createRawContact(account1);
8400b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov        Uri dataUri1 = insertStructuredName(rawContactId1, "John", "Doe");
8410b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov        long rawContactId2 = createRawContact(account2);
8420b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov        Uri dataUri2 = insertStructuredName(rawContactId2, "Jane", "Doe");
8430b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov
8440b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov        Uri uri1 = maybeAddAccountQueryParameters(dataUri1, account1);
8450b1eaf562411ffec26fd9113c3209ebdd29202e1Dmitri Plotnikov        Uri uri2 = maybeAddAccountQueryParameters(dataUri2, account2);
8465dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertStoredValue(uri1, Data._ID, ContentUris.parseId(dataUri1)) ;
8475dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        assertStoredValue(uri2, Data._ID, ContentUris.parseId(dataUri2)) ;
8485dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    }
8495dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
8505dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov    public void testPhonesQuery() {
8515dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
8525dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        ContentValues values = new ContentValues();
8535dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.CUSTOM_RINGTONE, "d");
8545dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
8555dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
8565dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.TIMES_CONTACTED, 54321);
8575dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        values.put(RawContacts.STARRED, 1);
8585dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov
8595dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
8605dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        long rawContactId = ContentUris.parseId(rawContactUri);
8614cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao
8624cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao        insertStructuredName(rawContactId, "Meghan", "Knox");
8635dd6d5d4acb93adc05f1fde904080787f2397f51Dmitri Plotnikov        Uri uri = insertPhoneNumber(rawContactId, "18004664411");
86401911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov        long phoneId = ContentUris.parseId(uri);
86501911fa9cfa21f198fd767eedde072acbb879f28Dmitri Plotnikov
86631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
86731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        long contactId = queryContactId(rawContactId);
86831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.clear();
86931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Data._ID, phoneId);
87031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Data.RAW_CONTACT_ID, rawContactId);
87131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(RawContacts.CONTACT_ID, contactId);
87231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
87331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Phone.NUMBER, "18004664411");
87431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Phone.TYPE, Phone.TYPE_HOME);
87531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.putNull(Phone.LABEL);
87631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
87731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Contacts.CUSTOM_RINGTONE, "d");
87831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Contacts.SEND_TO_VOICEMAIL, 1);
87931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Contacts.LAST_TIME_CONTACTED, 12345);
88031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Contacts.TIMES_CONTACTED, 54321);
88131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Contacts.STARRED, 1);
88231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
88331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        assertStoredValues(ContentUris.withAppendedId(Phone.CONTENT_URI, phoneId), values);
88431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        assertSelection(Phone.CONTENT_URI, values, Data._ID, phoneId);
88531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov    }
88631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
88731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov    public void testPhonesFilterQuery() {
88831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        long rawContactId1 = createRawContactWithName("Hot", "Tamale", ACCOUNT_1);
88931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        insertPhoneNumber(rawContactId1, "1-800-466-4411");
89031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
89131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        long rawContactId2 = createRawContactWithName("Chilled", "Guacamole", ACCOUNT_2);
89231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        insertPhoneNumber(rawContactId2, "1-800-466-5432");
89331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
89431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        Uri filterUri1 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "tamale");
89531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        ContentValues values = new ContentValues();
89631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
89731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
89831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Phone.NUMBER, "1-800-466-4411");
89931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.put(Phone.TYPE, Phone.TYPE_HOME);
90031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        values.putNull(Phone.LABEL);
90131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        assertStoredValuesWithProjection(filterUri1, values);
90231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
90331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        Uri filterUri2 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1-800-GOOG-411");
90431168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        assertStoredValues(filterUri2, values);
90531168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
90631168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        Uri filterUri3 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "18004664");
90731168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        assertStoredValues(filterUri3, values);
90831168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
90931168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        Uri filterUri4 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "encilada");
91031168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        assertEquals(0, getCount(filterUri4, null, null));
91131168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov
91231168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        Uri filterUri5 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "*");
91331168f49a3da9b9a9d5346f3d6a8098b76179c9cDmitri Plotnikov        assertEquals(0, getCount(filterUri5, null, null));
914916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    }
915916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
916916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    public void testPhoneLookup() {
917916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        ContentValues values = new ContentValues();
918916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(RawContacts.CUSTOM_RINGTONE, "d");
919916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
920916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
921916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
922916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        long rawContactId = ContentUris.parseId(rawContactUri);
923916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
9249c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        insertStructuredName(rawContactId, "Hot", "Tamale");
925916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        insertPhoneNumber(rawContactId, "18004664411");
926916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
927916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
928916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
929916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.clear();
930916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(PhoneLookup._ID, queryContactId(rawContactId));
931916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale");
932916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(PhoneLookup.NUMBER, "18004664411");
933916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(PhoneLookup.TYPE, Phone.TYPE_HOME);
934916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.putNull(PhoneLookup.LABEL);
935916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(PhoneLookup.CUSTOM_RINGTONE, "d");
936916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(PhoneLookup.SEND_TO_VOICEMAIL, 1);
937916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        assertStoredValues(lookupUri1, values);
938916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
939916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // In the context that 8004664411 is a valid number, "4664411" as a
940916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // call id should not match to "8004664411"
941916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411");
942916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        assertEquals(0, getCount(lookupUri2, null, null));
9439c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov    }
9449c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov
945916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov    public void testPhoneLookupUseCases() {
946916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        ContentValues values = new ContentValues();
947916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        Uri rawContactUri;
948916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        long rawContactId;
949916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        Uri lookupUri2;
950916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
951916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(RawContacts.CUSTOM_RINGTONE, "d");
952916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
953916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
954916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // International format in contacts
955916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
956916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        rawContactId = ContentUris.parseId(rawContactUri);
957916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
958916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        insertStructuredName(rawContactId, "Hot", "Tamale");
959916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        insertPhoneNumber(rawContactId, "+1-650-861-0000");
9609c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov
961916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.clear();
962916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
9639c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        // match with international format
9649c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0000");
965916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        assertEquals(1, getCount(lookupUri2, null, null));
966916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
967916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // match with national format
968916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0000");
969916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        assertEquals(1, getCount(lookupUri2, null, null));
970916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
971916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // National format in contacts
972916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.clear();
973916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(RawContacts.CUSTOM_RINGTONE, "d");
974916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
975916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
976916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        rawContactId = ContentUris.parseId(rawContactUri);
977916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
978916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        insertStructuredName(rawContactId, "Hot1", "Tamale");
9799c6ef008d92017108e3d10dcd8e2146eded9e148Dmitri Plotnikov        insertPhoneNumber(rawContactId, "650-861-0001");
980916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
981916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        values.clear();
982916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov
983916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        // match with international format
984916f2d7104bfba857412a66b40ed60fea6546222Dmitri Plotnikov        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0001");
985dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        assertEquals(2, getCount(lookupUri2, null, null));
986dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov
987dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        // match with national format
988dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0001");
989dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        assertEquals(2, getCount(lookupUri2, null, null));
990dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov
991dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        // Local format in contacts
992dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        values.clear();
993dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        values.put(RawContacts.CUSTOM_RINGTONE, "d");
994dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
995dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
996dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        rawContactId = ContentUris.parseId(rawContactUri);
997dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov
998dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        insertStructuredName(rawContactId, "Hot2", "Tamale");
999dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        insertPhoneNumber(rawContactId, "861-0002");
1000dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov
1001dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        values.clear();
1002dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov
1003dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        // match with international format
1004dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0002");
1005dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        assertEquals(1, getCount(lookupUri2, null, null));
1006dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov
1007dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        // match with national format
1008dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0002");
1009dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov        assertEquals(1, getCount(lookupUri2, null, null));
1010dd0e0f44fe403ff201d46d5534f7f1148e5ad729Dmitri Plotnikov    }
1011d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
1012d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov    public void testPhoneUpdate() {
1013d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        ContentValues values = new ContentValues();
1014d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1015d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        long rawContactId = ContentUris.parseId(rawContactUri);
1016d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
1017d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        insertStructuredName(rawContactId, "Hot", "Tamale");
1018d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        Uri phoneUri = insertPhoneNumber(rawContactId, "18004664411");
1019d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
1020d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
1021d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        assertStoredValue(lookupUri1, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
1022d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
1023d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        values.clear();
1024d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        values.put(Phone.NUMBER, "18004664422");
1025d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        mResolver.update(phoneUri, values, null, null);
1026d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov
1027d0f63551e3147babcebde5326b31285d7bdf6739Dmitri Plotnikov        Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664422");
1028d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertStoredValue(lookupUri2, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
1029c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov
1030d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        // Setting number to null will remove the phone lookup record
1031d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.clear();
1032d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        values.putNull(Phone.NUMBER);
1033d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        mResolver.update(phoneUri, values, null, null);
1034d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1035d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertEquals(0, getCount(lookupUri2, null, null));
1036d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1037d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Let's restore that phone lookup record
1038d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.clear();
1039d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(Phone.NUMBER, "18004664422");
1040c70dc0e38ff82c6e6d6b7458637c54fbdf446aacDmitri Plotnikov        mResolver.update(phoneUri, values, null, null);
1041d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        assertStoredValue(lookupUri2, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
1042d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertNetworkNotified(true);
1043d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    }
1044d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
104581d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    public void testEmailsQuery() {
10468c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        ContentValues values = new ContentValues();
10478c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        values.put(RawContacts.CUSTOM_RINGTONE, "d");
10488c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
10498c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
1050d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(RawContacts.TIMES_CONTACTED, 54321);
1051d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(RawContacts.STARRED, 1);
1052d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
10533cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1054d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        long rawContactId = ContentUris.parseId(rawContactUri);
1055d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1056d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        insertStructuredName(rawContactId, "Meghan", "Knox");
10573cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        Uri uri = insertEmail(rawContactId, "meghan@acme.com");
1058d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        long emailId = ContentUris.parseId(uri);
1059d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1060d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long contactId = queryContactId(rawContactId);
1061d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.clear();
10620c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        values.put(Data._ID, emailId);
10630c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        values.put(Data.RAW_CONTACT_ID, rawContactId);
1064d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(RawContacts.CONTACT_ID, contactId);
1065d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
1066d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        values.put(Email.DATA, "meghan@acme.com");
1067d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(Email.TYPE, Email.TYPE_HOME);
1068d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.putNull(Email.LABEL);
1069d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
10703cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        values.put(Contacts.CUSTOM_RINGTONE, "d");
1071d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        values.put(Contacts.SEND_TO_VOICEMAIL, 1);
1072d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        values.put(Contacts.LAST_TIME_CONTACTED, 12345);
1073d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(Contacts.TIMES_CONTACTED, 54321);
10743cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        values.put(Contacts.STARRED, 1);
1075d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1076d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values);
1077d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertSelection(Email.CONTENT_URI, values, Data._ID, emailId);
1078d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
10790c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
10800c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov    public void testEmailsLookupQuery() {
1081d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long rawContactId = createRawContactWithName("Hot", "Tamale");
1082d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        insertEmail(rawContactId, "tamale@acme.com");
10830c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov
1084d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "tamale@acme.com");
1085d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        ContentValues values = new ContentValues();
1086d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
10873cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
1088d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        values.put(Email.DATA, "tamale@acme.com");
1089d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        values.put(Email.TYPE, Email.TYPE_HOME);
1090d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.putNull(Email.LABEL);
10913cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        assertStoredValues(filterUri1, values);
1092d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1093d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "Ta<TaMale@acme.com>");
1094d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertStoredValues(filterUri2, values);
1095d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
10960c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "encilada@acme.com");
10970c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        assertEquals(0, getCount(filterUri3, null, null));
1098d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
1099d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
11000c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov    public void testEmailsFilterQuery() {
11010c0adda32be5de3acf392ab715cff468b6b340f8Dmitri Plotnikov        long rawContactId1 = createRawContactWithName("Hot", "Tamale", ACCOUNT_1);
1102d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        insertEmail(rawContactId1, "tamale@acme.com");
11033cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        insertEmail(rawContactId1, "tamale@acme.com");
1104d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1105d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long rawContactId2 = createRawContactWithName("Hot", "Tamale", ACCOUNT_2);
1106d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        insertEmail(rawContactId2, "tamale@acme.com");
110782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
11084dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tam");
11090a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        ContentValues values = new ContentValues();
11100a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
11110a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
11120a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        values.put(Email.DATA, "tamale@acme.com");
11130a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        values.put(Email.TYPE, Email.TYPE_HOME);
11140a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        values.putNull(Email.LABEL);
11150a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        assertStoredValuesWithProjection(filterUri1, values);
11160a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
11170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot");
11180a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        assertStoredValuesWithProjection(filterUri2, values);
11190a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
11200a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot tamale");
11210a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        assertStoredValuesWithProjection(filterUri3, values);
11220a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
11230a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        Uri filterUri4 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tamale@acme");
11240a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        assertStoredValuesWithProjection(filterUri4, values);
11250a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
11260a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        Uri filterUri5 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "encilada");
11270a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        assertEquals(0, getCount(filterUri5, null, null));
11280a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
11290a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
11300a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    /**
11310a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     * Tests if ContactsProvider2 returns addresses according to registration order.
11320a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     */
11330a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    public void testEmailFilterDefaultSortOrder() {
11340a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        long rawContactId1 = createRawContact();
11350a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        insertEmail(rawContactId1, "address1@email.com");
11360a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        insertEmail(rawContactId1, "address2@email.com");
11370a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        insertEmail(rawContactId1, "address3@email.com");
11380a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        ContentValues v1 = new ContentValues();
11390a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        v1.put(Email.ADDRESS, "address1@email.com");
11400a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        ContentValues v2 = new ContentValues();
11410a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        v2.put(Email.ADDRESS, "address2@email.com");
11420a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        ContentValues v3 = new ContentValues();
11430a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        v3.put(Email.ADDRESS, "address3@email.com");
11440a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
11450a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
11460a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        assertStoredValuesOrderly(filterUri, new ContentValues[] { v1, v2, v3 });
11470a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
11480a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
11490a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    /**
11500a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     * Tests if ContactsProvider2 returns primary addresses before the other addresses.
11510a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     */
11520a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    public void testEmailFilterPrimaryAddress() {
11530a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        long rawContactId1 = createRawContact();
11540a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        insertEmail(rawContactId1, "address1@email.com");
11550a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        insertEmail(rawContactId1, "address2@email.com", true);
11560a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        ContentValues v1 = new ContentValues();
11570a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        v1.put(Email.ADDRESS, "address1@email.com");
11580a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        ContentValues v2 = new ContentValues();
11590a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        v2.put(Email.ADDRESS, "address2@email.com");
11600a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
11610a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
11620a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        assertStoredValuesOrderly(filterUri, new ContentValues[] { v2, v1 });
11630a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
11640a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
11650a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    /**
11660a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     * Tests if ContactsProvider2 has email address associated with a primary account before the
11670a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     * other address.
11680a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov     */
11690a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    public void testEmailFilterPrimaryAccount() {
11700a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        long rawContactId1 = createRawContact(ACCOUNT_1);
11710a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        insertEmail(rawContactId1, "account1@email.com");
11720a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        long rawContactId2 = createRawContact(ACCOUNT_2);
11730a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        insertEmail(rawContactId2, "account2@email.com");
11740a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        ContentValues v1 = new ContentValues();
11750a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        v1.put(Email.ADDRESS, "account1@email.com");
11760a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        ContentValues v2 = new ContentValues();
11770a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        v2.put(Email.ADDRESS, "account2@email.com");
11780a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov
11790a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
11800a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_1.name)
11810a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, ACCOUNT_1.type)
11820a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov                .build();
11834dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2 });
11844dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
118582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
11864dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_2.name)
11874dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, ACCOUNT_2.type)
118882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                .build();
11894dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        assertStoredValuesOrderly(filterUri2, new ContentValues[] { v2, v1 });
11904dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
119182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        // Just with PRIMARY_ACCOUNT_NAME
11924dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
11934dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_1.name)
119482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                .build();
11954dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2 });
11964dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
119782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
11984dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov                .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_2.name)
119982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                .build();
120082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        assertStoredValuesOrderly(filterUri4, new ContentValues[] { v2, v1 });
12010a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov    }
120282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
12034dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov    /** Tests {@link DataUsageFeedback} correctly promotes a data row instead of a raw contact. */
120482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public void testEmailFilterSortOrderWithFeedback() {
12054dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        long rawContactId1 = createRawContact();
120682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        String address1 = "address1@email.com";
12074dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        insertEmail(rawContactId1, address1);
120882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        long rawContactId2 = createRawContact();
12094dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        String address2 = "address2@email.com";
12104dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        insertEmail(rawContactId2, address2);
1211bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        String address3 = "address3@email.com";
1212bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        ContentUris.parseId(insertEmail(rawContactId2, address3));
1213bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
1214bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        ContentValues v1 = new ContentValues();
1215bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        v1.put(Email.ADDRESS, "address1@email.com");
121682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        ContentValues v2 = new ContentValues();
12170a185cdcb65d1beb2a295fffbe2ae11a6a2c097fDmitri Plotnikov        v2.put(Email.ADDRESS, "address2@email.com");
1218bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        ContentValues v3 = new ContentValues();
1219bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        v3.put(Email.ADDRESS, "address3@email.com");
1220bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
122182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
1222bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
1223bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov                .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
1224bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov                        DataUsageFeedback.USAGE_TYPE_CALL)
1225bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov                .build();
1226bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
1227bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov                .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
1228bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov                        DataUsageFeedback.USAGE_TYPE_LONG_TEXT)
122982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                .build();
123082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
1231bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov                .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
1232bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov                        DataUsageFeedback.USAGE_TYPE_SHORT_TEXT)
123382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                .build();
123482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2, v3 });
123582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        assertStoredValuesOrderly(filterUri2, new ContentValues[] { v1, v2, v3 });
123682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2, v3 });
123782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        assertStoredValuesOrderly(filterUri4, new ContentValues[] { v1, v2, v3 });
1238bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov
123982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        sendFeedback(address3, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, v3);
124082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
1241bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        // account3@email.com should be the first. account2@email.com should also be promoted as
1242bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        // it has same contact id.
12439705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        assertStoredValuesOrderly(filterUri1, new ContentValues[] { v3, v1, v2 });
12449705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        assertStoredValuesOrderly(filterUri3, new ContentValues[] { v3, v1, v2 });
12459705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    }
12469705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
12479705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    /**
12489705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * Tests {@link DataUsageFeedback} correctly bucketize contacts using each
12499705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     * {@link DataUsageStatColumns#LAST_TIME_USED}
12509705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori     */
12519705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori    public void testEmailFilterSortOrderWithOldHistory() {
12529705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        long rawContactId1 = createRawContact();
12539705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        long dataId1 = ContentUris.parseId(insertEmail(rawContactId1, "address1@email.com"));
12549705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        long dataId2 = ContentUris.parseId(insertEmail(rawContactId1, "address2@email.com"));
12559705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        long dataId3 = ContentUris.parseId(insertEmail(rawContactId1, "address3@email.com"));
12569705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        long dataId4 = ContentUris.parseId(insertEmail(rawContactId1, "address4@email.com"));
12579705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
12589705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
12599705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
12609705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues v1 = new ContentValues();
12619705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        v1.put(Email.ADDRESS, "address1@email.com");
12629705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues v2 = new ContentValues();
12639705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        v2.put(Email.ADDRESS, "address2@email.com");
12649705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues v3 = new ContentValues();
12659705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        v3.put(Email.ADDRESS, "address3@email.com");
12669705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        ContentValues v4 = new ContentValues();
12679705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        v4.put(Email.ADDRESS, "address4@email.com");
12689705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
12699705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        final ContactsProvider2 provider = (ContactsProvider2) getProvider();
12709705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
12719705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        long nowInMillis = System.currentTimeMillis();
12729705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        long yesterdayInMillis = (nowInMillis - 24 * 60 * 60 * 1000);
12739705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        long sevenDaysAgoInMillis = (nowInMillis - 7 * 24 * 60 * 60 * 1000);
12749705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        long oneYearAgoInMillis = (nowInMillis - 365L * 24 * 60 * 60 * 1000);
12759705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
12769705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // address4 is contacted just once yesterday.
12779705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        provider.updateDataUsageStat(Arrays.asList(dataId4),
12789705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, yesterdayInMillis);
12799705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
12809705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // address3 is contacted twice 1 week ago.
12819705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        provider.updateDataUsageStat(Arrays.asList(dataId3),
12829705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis);
12839705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        provider.updateDataUsageStat(Arrays.asList(dataId3),
12849705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis);
12859705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori
12869705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori        // address2 is contacted three times 1 year ago.
128782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        provider.updateDataUsageStat(Arrays.asList(dataId2),
12889705f5bcb04c4b3012a762fb3ba8620b518587ccVasu Nori                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
128982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        provider.updateDataUsageStat(Arrays.asList(dataId2),
1290a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
1291a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        provider.updateDataUsageStat(Arrays.asList(dataId2),
1292a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
129382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
1294a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        // auto-complete should prefer recently contacted methods
1295a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        assertStoredValuesOrderly(filterUri1, new ContentValues[] { v4, v3, v2, v1 });
1296a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1297a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        // Pretend address2 is contacted right now
1298a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        provider.updateDataUsageStat(Arrays.asList(dataId2),
1299a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis);
130082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
130182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        // Now address2 is the most recently used address
130282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v4, v3, v1 });
1303a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov
1304a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        // Pretend address1 is contacted right now
1305a23cd5b6f478f6c9dda54173e84bd0098b9f3364Dmitri Plotnikov        provider.updateDataUsageStat(Arrays.asList(dataId1),
130682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis);
130782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
1308bffeabdf3dcf58f963ad1bb4d3e6e51f3ac16cfdDmitri Plotnikov        // address2 is preferred to address1 as address2 is used 4 times in total
13094dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v1, v4, v3 });
13104dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov    }
131182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
131282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    public void testPostalsQuery() {
13134dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        long rawContactId = createRawContactWithName("Alice", "Nextore");
131482bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");
131582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        long dataId = ContentUris.parseId(dataUri);
131682bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
131782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        long contactId = queryContactId(rawContactId);
13184dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        ContentValues values = new ContentValues();
13194dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        values.put(Data._ID, dataId);
13204dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        values.put(Data.RAW_CONTACT_ID, rawContactId);
132182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        values.put(RawContacts.CONTACT_ID, contactId);
1322bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
1323bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        values.put(StructuredPostal.FORMATTED_ADDRESS, "1600 Amphiteatre Ave, Mountain View");
1324bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        values.put(Contacts.DISPLAY_NAME, "Alice Nextore");
1325d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
13264dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId),
1327bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                values);
132882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        assertSelection(StructuredPostal.CONTENT_URI, values, Data._ID, dataId);
132982bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
133082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
1331bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar    public void testQueryContactData() {
1332af088aeb51685eed17580edc04b495d12232ecf9Dmitri Plotnikov        ContentValues values = new ContentValues();
133382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        long contactId = createContact(values, "John", "Doe",
13344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
1335bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
1336bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
133782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
133882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        assertStoredValues(contactUri, values);
13390265a180cf027d149f11f8750652ac67ea08ca24Dmitri Plotnikov        assertSelection(Contacts.CONTENT_URI, values, Contacts._ID, contactId);
1340bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar    }
1341bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar
1342d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    public void testQueryContactWithStatusUpdate() {
1343d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        ContentValues values = new ContentValues();
1344d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long contactId = createContact(values, "John", "Doe",
1345d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov                "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
1346d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_CAMERA);
1347d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
1348d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(Contacts.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
1349d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1350d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        assertStoredValuesWithProjection(contactUri, values);
1351d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertSelectionWithProjection(Contacts.CONTENT_URI, values, Contacts._ID, contactId);
1352d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
13538c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
13548c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov    public void testQueryContactFilterByName() {
13558c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        ContentValues values = new ContentValues();
13568c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        long rawContactId = createRawContact(values, "18004664411",
13578c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
13588c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
13598c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_VOICE);
13608c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov
13618c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        ContentValues nameValues = new ContentValues();
13628c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        nameValues.put(StructuredName.GIVEN_NAME, "Stu");
13638c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        nameValues.put(StructuredName.FAMILY_NAME, "Goulash");
13648c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "goo");
13658c4f838f899daadb6f46f8c27ab7636023e39c38Dmitri Plotnikov        nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "LASH");
1366d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        Uri nameUri = insertStructuredName(rawContactId, nameValues);
1367d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1368d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        long contactId = queryContactId(rawContactId);
1369d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
1370d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1371d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goulash");
1372d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        assertStoredValuesWithProjection(filterUri1, values);
1373d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1374d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        assertContactFilter(contactId, "goolash");
1375d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertContactFilter(contactId, "lash");
1376d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1377d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertContactFilterNoResult("goolish");
1378d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1379d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Phonetic name with given/family reversed should not match
1380d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertContactFilterNoResult("lashgoo");
1381d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
13829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        nameValues.clear();
13839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "ga");
1384d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "losh");
1385d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
13869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        mResolver.update(nameUri, nameValues, null, null);
13879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
13889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertContactFilter(contactId, "galosh");
1389d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
13909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertContactFilterNoResult("goolish");
13919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
13929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
1393d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov    public void testQueryContactFilterByEmailAddress() {
13949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        ContentValues values = new ContentValues();
1395d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        long rawContactId = createRawContact(values, "18004664411",
13969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
13979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
13989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                StatusUpdates.CAPABILITY_HAS_VOICE);
1399d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
14009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        insertStructuredName(rawContactId, "James", "Bond");
14019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
14029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long contactId = queryContactId(rawContactId);
1403d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
14049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
14059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goog411@acme.com");
14069261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertStoredValuesWithProjection(filterUri1, values);
1407d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
14089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertContactFilter(contactId, "goog");
14099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertContactFilter(contactId, "goog411");
14109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertContactFilter(contactId, "goog411@");
14119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertContactFilter(contactId, "goog411@acme");
14129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertContactFilter(contactId, "goog411@acme.com");
14139261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
14149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertContactFilterNoResult("goog411@acme.combo");
14159261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertContactFilterNoResult("goog411@le.com");
14169261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertContactFilterNoResult("goolish");
14179261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
14189261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
14190be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov    public void testQueryContactFilterByPhoneNumber() {
14200be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        ContentValues values = new ContentValues();
14210be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        long rawContactId = createRawContact(values, "18004664411",
14220be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov                "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
14230be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
14240be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_VOICE);
14250be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
14260be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        insertStructuredName(rawContactId, "James", "Bond");
14270be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
14280be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        long contactId = queryContactId(rawContactId);
14290be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
14300be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
14310be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "18004664411");
14320be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        assertStoredValuesWithProjection(filterUri1, values);
14330be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
14340be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        assertContactFilter(contactId, "18004664411");
14350be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        assertContactFilter(contactId, "1800466");
14360be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        assertContactFilter(contactId, "+18004664411");
14370be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        assertContactFilter(contactId, "8004664411");
14380be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
14390be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        assertContactFilterNoResult("78004664411");
14400be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        assertContactFilterNoResult("18004664412");
14410be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov        assertContactFilterNoResult("8884664411");
14420be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov    }
14430be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov
14440be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov    /**
14450be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov     * Checks ContactsProvider2 works well with strequent Uris. The provider should return starred
14460be993f8ef0078b9825a5ffe6add08a6786d8dacDmitri Plotnikov     * contacts and frequently used contacts.
14479261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
14489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    public void testQueryContactStrequent() {
14499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        ContentValues values1 = new ContentValues();
14509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        final String email1 = "a@acme.com";
14519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        final int timesContacted1 = 0;
14529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        createContact(values1, "Noah", "Tever", "18004664411",
14539261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                email1, StatusUpdates.OFFLINE, timesContacted1, 0, 0,
14543cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
14553cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        final String phoneNumber2 = "18004664412";
14563cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        ContentValues values2 = new ContentValues();
14573cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        createContact(values2, "Sam", "Times", phoneNumber2,
14589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                "b@acme.com", StatusUpdates.INVISIBLE, 3, 0, 0,
1459226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                StatusUpdates.CAPABILITY_HAS_CAMERA);
14609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        ContentValues values3 = new ContentValues();
14619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        final String phoneNumber3 = "18004664413";
14629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        final int timesContacted3 = 5;
14639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        createContact(values3, "Lotta", "Calling", phoneNumber3,
14649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                "c@acme.com", StatusUpdates.AWAY, timesContacted3, 0, 0,
1465226c3dc6e93ca76a84c99100caa31045cba06cf6Fred Quintana                StatusUpdates.CAPABILITY_HAS_VIDEO);
14669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        ContentValues values4 = new ContentValues();
14679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        createContact(values4, "Fay", "Veritt", "18004664414",
14689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                "d@acme.com", StatusUpdates.AVAILABLE, 0, 1, 0,
14699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                StatusUpdates.CAPABILITY_HAS_VIDEO | StatusUpdates.CAPABILITY_HAS_VOICE);
14703cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov
14719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Starred contacts should be returned. TIMES_CONTACTED should be ignored and only data
14729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // usage feedback should be used for "frequently contacted" listing.
14739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertStoredValues(Contacts.CONTENT_STREQUENT_URI, values4);
14749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
147562318e1ea8306142a10526534b7d83560ecf5b3aFred Quintana        // Send feedback for the 3rd phone number, pretending we called that person via phone.
147662318e1ea8306142a10526534b7d83560ecf5b3aFred Quintana        sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
147762318e1ea8306142a10526534b7d83560ecf5b3aFred Quintana
14789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // After the feedback, 3rd contact should be shown after starred one.
14799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI,
14809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                new ContentValues[] { values4, values3 });
14816cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov
14829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
14839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Twice.
14849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
14859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
14869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // After the feedback, 1st and 3rd contacts should be shown after starred one.
14879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI,
14889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                new ContentValues[] { values4, values1, values3 });
14899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
14909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // With phone-only parameter, the 1st contact shouldn't be returned, since it is only
14919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // about email, not phone-call.
14929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri phoneOnlyStrequentUri = Contacts.CONTENT_STREQUENT_URI.buildUpon()
14939261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true")
14949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                .build();
14959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { values4, values3 });
14969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
14979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Send feedback for the 2rd phone number, pretending we send the person a SMS message.
14989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sendFeedback(phoneNumber2, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1);
14999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
15006cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        // SMS feedback shouldn't affect phone-only results.
15019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { values4, values3 });
15029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
15039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_STREQUENT_FILTER_URI, "fay");
15049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertStoredValues(filterUri, values4);
15059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
15069261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
15079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
15089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * Checks ContactsProvider2 works well with frequent Uri. The provider should return frequently
15099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * contacted person ordered by number of times contacted.
15109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
15119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    public void testQueryContactFrequent() {
15129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        ContentValues values1 = new ContentValues();
15139261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        final String email1 = "a@acme.com";
15149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        createContact(values1, "Noah", "Tever", "18004664411",
15156cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov                email1, StatusUpdates.OFFLINE, 0, 0, 0, 0);
15169261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        ContentValues values2 = new ContentValues();
15179261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        final String email2 = "b@acme.com";
15189261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        createContact(values2, "Sam", "Times", "18004664412",
15199261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0);
15209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        ContentValues values3 = new ContentValues();
15219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        final String phoneNumber3 = "18004664413";
15229261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        createContact(values3, "Lotta", "Calling", phoneNumber3,
15239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                "c@acme.com", StatusUpdates.AWAY, 0, 0, 0, 0);
15249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        ContentValues values4 = new ContentValues();
15259261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        createContact(values4, "Fay", "Veritt", "18004664414",
15269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                "d@acme.com", StatusUpdates.AVAILABLE, 0, 1, 0, 0);
15279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
15289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
15299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
15303cfe8d532d509fbbe605454e3a32b2361b7e1501Dmitri Plotnikov        assertStoredValues(Contacts.CONTENT_FREQUENT_URI, values1);
15319261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
153220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Pretend email was sent to the address twice.
153320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2);
1534d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2);
153520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
153620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values2, values1});
15375ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov
153820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Three times
153920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
154020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
154120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
154220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
154320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
154420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                new ContentValues[] {values3, values2, values1});
154520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
154620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
154720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public void testQueryContactGroup() {
154820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        long groupId = createGroup(null, "testGroup", "Test Group");
154920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
155020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        ContentValues values1 = new ContentValues();
155120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        createContact(values1, "Best", "West", "18004664411",
155220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                "west@acme.com", StatusUpdates.OFFLINE, 0, 0, groupId,
155320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_CAMERA);
155420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
155520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        ContentValues values2 = new ContentValues();
155620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        createContact(values2, "Rest", "East", "18004664422",
155720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                "east@acme.com", StatusUpdates.AVAILABLE, 0, 0, 0,
155820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_VOICE);
155981d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
156020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
156120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        Cursor c = mResolver.query(filterUri1, null, null, null, Contacts._ID);
156220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        assertEquals(1, c.getCount());
156320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        c.moveToFirst();
156420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        assertCursorValues(c, values1);
156520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        c.close();
156620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
156720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
156820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        c = mResolver.query(filterUri2, null, Contacts.DISPLAY_NAME + "=?",
156920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                new String[] { "Best West" }, Contacts._ID);
157020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        assertEquals(1, c.getCount());
157120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        c.close();
157220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
157320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Next Group");
157420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        c = mResolver.query(filterUri3, null, null, null, Contacts._ID);
157520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        assertEquals(0, c.getCount());
157620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        c.close();
157720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
157820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
157920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public void testQueryProfileRequiresReadPermission() {
15805ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        mActor.removePermissions("android.permission.READ_PROFILE");
158120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
158281d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        createBasicProfileContact(new ContentValues());
158370b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
158470b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        // Queries for the profile should fail.
158570b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        Cursor c = null;
158670b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
158720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        // Case 1: Retrieving profile contact.
158820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
15895ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov            c = mResolver.query(Profile.CONTENT_URI, null, null, null, Contacts._ID);
159020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            fail("Querying for the profile without READ_PROFILE access should fail.");
159120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } catch (SecurityException expected) {
15925ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov        } finally {
159333b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov            if (c != null) {
159481d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                c.close();
159533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov            }
159620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
159789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov
159889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        // Case 2: Retrieving profile data.
159989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        try {
160089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            c = mResolver.query(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
160189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                    null, null, null, Contacts._ID);
160289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            fail("Querying for the profile data without READ_PROFILE access should fail.");
160389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        } catch (SecurityException expected) {
160489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        } finally {
160589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            if (c != null) {
160689c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                c.close();
160789c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            }
160889c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        }
160989c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov
161089c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        // Case 3: Retrieving profile entities.
161189c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        try {
161289c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            c = mResolver.query(Profile.CONTENT_URI.buildUpon()
161389c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov                    .appendPath("entities").build(), null, null, null, Contacts._ID);
161489c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov            fail("Querying for the profile entities without READ_PROFILE access should fail.");
161589c626eb655440c86a2e5df076e83708c1b32c17Dmitri Plotnikov        } catch (SecurityException expected) {
1616373f7d2adc36680c31ff33e9ee12be865af6b5fbDmitri Plotnikov        } finally {
1617e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (c != null) {
16185ef0401c311c62e53bde415b99cbb0ff83b0a9a2Dmitri Plotnikov                c.close();
161933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov            }
16204dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov        }
162182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov    }
162282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
1623a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov    public void testQueryProfileByContactIdRequiresReadPermission() {
1624a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov        long profileRawContactId = createBasicProfileContact(new ContentValues());
162533b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        long profileContactId = queryContactId(profileRawContactId);
162633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov
162782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        mActor.removePermissions("android.permission.READ_PROFILE");
16284dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov
162933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        // A query for the profile contact by ID should fail.
163033b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        Cursor c = null;
163133b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        try {
16325870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov            c = mResolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId),
163381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                    null, null, null, Contacts._ID);
163433b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov            fail("Querying for the profile by contact ID without READ_PROFILE access should fail.");
1635e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        } catch (SecurityException expected) {
163633b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov        } finally {
163733b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov            if (c != null) {
163833b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov                c.close();
163933b41fdb8d7c3c654cb070799c9d6e2b4ab16078Dmitri Plotnikov            }
164082bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        }
16414dcd106ccc27dbbfaae86baf0cd57beb42c27cccDmitri Plotnikov    }
1642a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov
164381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov    public void testQueryProfileByRawContactIdRequiresReadPermission() {
1644a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov        long profileRawContactId = createBasicProfileContact(new ContentValues());
1645a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov
1646a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov        // Remove profile read permission and attempt to retrieve the raw contact.
1647e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        mActor.removePermissions("android.permission.READ_PROFILE");
1648e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        Cursor c = null;
1649a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov        try {
1650a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov            c = mResolver.query(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
1651a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov                    profileRawContactId), null, null, null, RawContacts._ID);
1652a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov            fail("Querying for the raw contact profile without READ_PROFILE access should fail.");
1653a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov        } catch (SecurityException expected) {
1654a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov        } finally {
1655a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov            if (c != null) {
1656e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey                c.close();
1657a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov            }
1658a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov        }
1659a5bfaf55790262eea97de432d9e7f313c219c066Dmitri Plotnikov    }
166020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
16611fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana    public void testQueryProfileRawContactRequiresReadPermission() {
1662e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        long profileRawContactId = createBasicProfileContact(new ContentValues());
1663e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
1664e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        // Remove profile read permission and attempt to retrieve the profile's raw contact data.
1665e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        mActor.removePermissions("android.permission.READ_PROFILE");
1666e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        Cursor c = null;
166782bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
166882bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        // Case 1: Retrieve the overall raw contact set for the profile.
1669e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        try {
1670e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            c = mResolver.query(Profile.CONTENT_RAW_CONTACTS_URI, null, null, null, null);
167182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            fail("Querying for the raw contact profile without READ_PROFILE access should fail.");
1672e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } catch (SecurityException expected) {
1673e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } finally {
1674e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (c != null) {
1675e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                c.close();
1676e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
1677e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
1678e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
1679e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        // Case 2: Retrieve the raw contact profile data for the inserted raw contact ID.
1680e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        try {
1681e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            c = mResolver.query(ContentUris.withAppendedId(
16825870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov                    Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
1683e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                    .appendPath("data").build(), null, null, null, null);
1684e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            fail("Querying for the raw profile data without READ_PROFILE access should fail.");
1685e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } catch (SecurityException expected) {
1686e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        } finally {
1687e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            if (c != null) {
1688e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong                c.close();
1689e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            }
1690e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        }
1691e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong
16925870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        // Case 3: Retrieve the raw contact profile entity for the inserted raw contact ID.
1693e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong        try {
1694e2579e029472f76b2dfda141444d775c67da0ec8Cynthia Wong            c = mResolver.query(ContentUris.withAppendedId(
1695627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov                    Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
169670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                    .appendPath("entity").build(), null, null, null, null);
169770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            fail("Querying for the raw profile entities without READ_PROFILE access should fail.");
169870d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } catch (SecurityException expected) {
169970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        } finally {
170070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            if (c != null) {
170182bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov                c.close();
170282bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov            }
170370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        }
170470d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    }
170570d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong
170670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong    public void testQueryProfileDataByDataIdRequiresReadPermission() {
170770d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        createBasicProfileContact(new ContentValues());
1708627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        Cursor c = mResolver.query(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
170970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                new String[]{Data._ID, Data.MIMETYPE}, null, null, null);
171070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        assertEquals(4, c.getCount());  // Photo, phone, email, name.
171170d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        c.moveToFirst();
171270d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        long profileDataId = c.getLong(0);
171370d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        c.close();
1714627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov
171582bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov        // Remove profile read permission and attempt to retrieve the data
171670d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong        mActor.removePermissions("android.permission.READ_PROFILE");
1717627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov        try {
1718627152453c692915ac79191acd1d2d2a4dd6fb0dDmitri Plotnikov            c = mResolver.query(ContentUris.withAppendedId(Data.CONTENT_URI, profileDataId),
171970d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong                    null, null, null, null);
172070d2ff8c87961703351b223ce8b15342fe795a0bCynthia Wong            fail("Querying for the data in the profile without READ_PROFILE access should fail.");
1721cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } catch (SecurityException expected) {
1722cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        } finally {
1723cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            if (c != null) {
1724cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov                c.close();
1725cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov            }
1726cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        }
1727cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    }
1728cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
1729cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov    public void testQueryProfileDataRequiresReadPermission() {
1730cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        createBasicProfileContact(new ContentValues());
1731cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov
1732cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        // Remove profile read permission and attempt to retrieve all profile data.
1733cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        mActor.removePermissions("android.permission.READ_PROFILE");
1734cb144e1429596701603c016f4a078f6331e6481dDmitri Plotnikov        Cursor c = null;
173573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        try {
173673776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            c = mResolver.query(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
173773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                    null, null, null, null);
173873776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            fail("Querying for the data in the profile without READ_PROFILE access should fail.");
173973776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } catch (SecurityException expected) {
174073776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        } finally {
1741e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey            if (c != null) {
174273776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov                c.close();
174373776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov            }
174473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        }
174573776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    }
17465870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov
174773776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov    public void testInsertProfileRequiresWritePermission() {
174881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mActor.removePermissions("android.permission.WRITE_PROFILE");
17491fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana
17501fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        // Creating a non-profile contact should be fine.
175161d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        createBasicNonProfileContact(new ContentValues());
1752d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov
1753d3fde755e73cd3912a488e7cb7d412d3c5f6ca94Dmitri Plotnikov        // Creating a profile contact should throw an exception.
175473776ffd5c00e94db987ee30864e9c7a8396d22dDmitri Plotnikov        try {
17551fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana            createBasicProfileContact(new ContentValues());
17561fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana            fail("Creating a profile contact should fail without WRITE_PROFILE access.");
17571fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        } catch (SecurityException expected) {
17581fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        }
17591fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana    }
176061d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov
1761c100221f706afc08409e8317a27d6850b11c54d3Omari Stephens    public void testInsertProfileDataRequiresWritePermission() {
176261d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        long profileRawContactId = createBasicProfileContact(new ContentValues());
17631fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana
17641fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        mActor.removePermissions("android.permission.WRITE_PROFILE");
17651fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        try {
17661fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana            insertEmail(profileRawContactId, "foo@bar.net", false);
176781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov            fail("Inserting data into a profile contact should fail without WRITE_PROFILE access.");
17681fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        } catch (SecurityException expected) {
176961d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        }
177061d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov    }
177181d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
17721fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana    public void testUpdateDataDoesNotRequireProfilePermission() {
17731fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        mActor.removePermissions("android.permission.READ_PROFILE");
177461d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        mActor.removePermissions("android.permission.WRITE_PROFILE");
17751fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana
177661d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        // Create a non-profile contact.
177761d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        long rawContactId = createRawContactWithName("Domo", "Arigato");
177861d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        long dataId = getStoredLongValue(Data.CONTENT_URI,
17791fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana                Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
178081d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov                new String[]{String.valueOf(rawContactId), StructuredName.CONTENT_ITEM_TYPE},
17811fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana                Data._ID);
17821fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana
178361d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        // Updates its name using a selection.
17841fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        ContentValues values = new ContentValues();
178561d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        values.put(StructuredName.GIVEN_NAME, "Bob");
17861fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        values.put(StructuredName.FAMILY_NAME, "Blob");
178781d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?",
17881fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana                new String[]{String.valueOf(dataId)});
17891fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana
179061d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        // Check that the update went through.
17911fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        assertStoredValues(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), values);
179261d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov    }
179361d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov
179461d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov    public void testQueryContactIncludeProfile() {
179561d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        ContentValues profileValues = new ContentValues();
179661d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        long profileRawContactId = createBasicProfileContact(profileValues);
179761d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        long profileContactId = queryContactId(profileRawContactId);
17981fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana
179961d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        ContentValues nonProfileValues = new ContentValues();
18001fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        long nonProfileRawContactId = createBasicNonProfileContact(nonProfileValues);
18011fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        long nonProfileContactId = queryContactId(nonProfileRawContactId);
18021fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana
18031fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        Uri contactWithProfilesUri = Contacts.CONTENT_URI.buildUpon()
18041fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana                .appendQueryParameter(ContactsContract.ALLOW_PROFILE, "1").build();
18051fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana        assertStoredValuesOrderly(contactWithProfilesUri,
18061fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana                new ContentValues[]{profileValues, nonProfileValues});
180761d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        assertSelection(contactWithProfilesUri, profileValues, Contacts._ID, profileContactId);
180861d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        assertSelection(Contacts.CONTENT_URI, nonProfileValues, Contacts._ID, nonProfileContactId);
18091fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana    }
181061d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov
18111fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana    public void testQueryContactExcludeProfile() {
181261d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        // Create a profile contact (it should not be returned by the general contact URI).
181361d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        createBasicProfileContact(new ContentValues());
181461d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov
181561d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        // Create a non-profile contact - this should be returned.
18165870f2dcc2ac7715b2c078a886ee346622e7887eDmitri Plotnikov        ContentValues nonProfileValues = new ContentValues();
181761d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        createBasicNonProfileContact(nonProfileValues);
181881d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov
181961d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov        assertStoredValues(Contacts.CONTENT_URI, new ContentValues[] {nonProfileValues});
182061d61cb4d13a33c5581765fb4c0f1b3c0b2cdf4cDmitri Plotnikov    }
18211fd9b53d9e5d8ea87b69a51fb084c6f0d9f7c93eFred Quintana
18224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    public void testQueryProfile() {
18239fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        ContentValues profileValues = new ContentValues();
18249fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        createBasicProfileContact(profileValues);
18259fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
18269fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        assertStoredValues(Profile.CONTENT_URI, profileValues);
18279fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann    }
18289fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
18299fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann    private ContentValues[] getExpectedProfileDataValues() {
18309fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        // Expected photo data values (only field is the photo BLOB, which we can't check).
18319fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        ContentValues photoRow = new ContentValues();
18329fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        photoRow.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
18339fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
18349fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        // Expected phone data values.
18359fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        ContentValues phoneRow = new ContentValues();
18369fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        phoneRow.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
18379fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        phoneRow.put(Phone.NUMBER, "18005554411");
18389fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
18399fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        // Expected email data values.
18409fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        ContentValues emailRow = new ContentValues();
18419fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        emailRow.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
18429fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        emailRow.put(Email.ADDRESS, "mia.prophyl@acme.com");
18439fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann
18449fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        // Expected name data values.
18459fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        ContentValues nameRow = new ContentValues();
18469fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        nameRow.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
18479fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        nameRow.put(StructuredName.DISPLAY_NAME, "Mia Prophyl");
18489fcf109b56cec0aad05322a3b4594228ea06d859Daniel Lehmann        nameRow.put(StructuredName.GIVEN_NAME, "Mia");
184960de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        nameRow.put(StructuredName.FAMILY_NAME, "Prophyl");
185060de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann
185160de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        return new ContentValues[]{photoRow, phoneRow, emailRow, nameRow};
185260de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann    }
185360de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann
185460de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann    public void testQueryProfileData() {
185560de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        createBasicProfileContact(new ContentValues());
185660de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann
185760de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
185860de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                getExpectedProfileDataValues());
185960de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann    }
186060de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann
186160de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann    public void testQueryProfileEntities() {
186260de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        createBasicProfileContact(new ContentValues());
186360de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann
186460de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("entities").build(),
186560de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann                getExpectedProfileDataValues());
186660de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann    }
186760de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann
186860de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann    public void testQueryRawProfile() {
186960de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        ContentValues profileValues = new ContentValues();
187060de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        createBasicProfileContact(profileValues);
187160de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann
187260de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        // The raw contact view doesn't include the photo ID.
187360de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        profileValues.remove(Contacts.PHOTO_ID);
187460de6f6c3c70e53b603a47b0efc80993353a8368Daniel Lehmann        assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, profileValues);
1875074cf38e39d500e92fa851a171d0378ab2c528c2Dmitri Plotnikov    }
1876074cf38e39d500e92fa851a171d0378ab2c528c2Dmitri Plotnikov
1877074cf38e39d500e92fa851a171d0378ab2c528c2Dmitri Plotnikov    public void testQueryRawProfileById() {
1878074cf38e39d500e92fa851a171d0378ab2c528c2Dmitri Plotnikov        ContentValues profileValues = new ContentValues();
1879074cf38e39d500e92fa851a171d0378ab2c528c2Dmitri Plotnikov        long profileRawContactId = createBasicProfileContact(profileValues);
1880074cf38e39d500e92fa851a171d0378ab2c528c2Dmitri Plotnikov
1881074cf38e39d500e92fa851a171d0378ab2c528c2Dmitri Plotnikov        // The raw contact view doesn't include the photo ID.
18823653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov        profileValues.remove(Contacts.PHOTO_ID);
18838e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        assertStoredValues(ContentUris.withAppendedId(
1884074cf38e39d500e92fa851a171d0378ab2c528c2Dmitri Plotnikov                Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId), profileValues);
18853653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov    }
18863653cf1fa8fb36a96a7e4a6ebd615438877c3183Dmitri Plotnikov
1887074cf38e39d500e92fa851a171d0378ab2c528c2Dmitri Plotnikov    public void testQueryRawProfileData() {
1888074cf38e39d500e92fa851a171d0378ab2c528c2Dmitri Plotnikov        long profileRawContactId = createBasicProfileContact(new ContentValues());
1889732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov
1890732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        assertStoredValues(ContentUris.withAppendedId(
1891732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov                Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
1892732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov                .appendPath("data").build(), getExpectedProfileDataValues());
1893732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov    }
1894732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov
1895732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov    public void testQueryRawProfileEntity() {
1896732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        long profileRawContactId = createBasicProfileContact(new ContentValues());
1897732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov
1898732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        assertStoredValues(ContentUris.withAppendedId(
1899732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov                Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
1900732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov                .appendPath("entity").build(), getExpectedProfileDataValues());
1901732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov    }
1902732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov
1903732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov    public void testQueryDataForProfile() {
1904732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        createBasicProfileContact(new ContentValues());
1905732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov
1906732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
1907732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov                getExpectedProfileDataValues());
1908732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov    }
1909732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov
1910732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov    public void testPhonesWithStatusUpdate() {
1911732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov
1912732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        ContentValues values = new ContentValues();
1913732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1914732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        long rawContactId = ContentUris.parseId(rawContactUri);
1915732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        insertStructuredName(rawContactId, "John", "Doe");
1916732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        Uri photoUri = insertPhoto(rawContactId);
1917732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        long photoId = ContentUris.parseId(photoUri);
1918732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        insertPhoneNumber(rawContactId, "18004664411");
1919732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        insertPhoneNumber(rawContactId, "18004664412");
1920732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        insertEmail(rawContactId, "goog411@acme.com");
1921732961a5b936d316482f9ded6bfc5fe1c99a65c8Dmitri Plotnikov        insertEmail(rawContactId, "goog412@acme.com");
19228e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
19238e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411@acme.com",
19248e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                StatusUpdates.INVISIBLE, "Bad",
19258e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_CAMERA);
19268e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog412@acme.com",
19278e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                StatusUpdates.AVAILABLE, "Good",
19288e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VOICE);
19298e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        long contactId = queryContactId(rawContactId);
19308e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
19318e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        Uri uri = Data.CONTENT_URI;
19328e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
19338e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        Cursor c = mResolver.query(uri, null, RawContacts.CONTACT_ID + "=" + contactId + " AND "
19348e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov                + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'", null, Phone.NUMBER);
19358e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        assertEquals(2, c.getCount());
19368e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
19378e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        c.moveToFirst();
19388e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
19398e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        values.clear();
19408e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
19418e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        values.put(Contacts.CONTACT_STATUS, "Bad");
19428e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        values.put(Contacts.DISPLAY_NAME, "John Doe");
194381d6a78dffd57f24f9aaecb6cd54e4084c3c9846Dmitri Plotnikov        values.put(Phone.NUMBER, "18004664411");
19448e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        values.putNull(Phone.LABEL);
19458e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        values.put(RawContacts.CONTACT_ID, contactId);
19468e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        assertCursorValues(c, values);
19478e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov
19488e45e5f2142db78941b095f7418cc05b71668094Dmitri Plotnikov        c.moveToNext();
19494e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov
19507d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.clear();
19517d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
19527d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.put(Contacts.CONTACT_STATUS, "Bad");
19537d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.put(Contacts.DISPLAY_NAME, "John Doe");
19547d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.put(Phone.NUMBER, "18004664412");
19557d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.putNull(Phone.LABEL);
19567d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.put(RawContacts.CONTACT_ID, contactId);
19577d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        assertCursorValues(c, values);
19587d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
19597d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        c.close();
19607d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh    }
19617d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
19627d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh    public void testGroupQuery() {
19637d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        Account account1 = new Account("a", "b");
19647d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        Account account2 = new Account("c", "d");
19657d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        long groupId1 = createGroup(account1, "e", "f");
19667d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        long groupId2 = createGroup(account2, "g", "h");
19677d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        Uri uri1 = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account1);
19687d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        Uri uri2 = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account2);
19697d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        assertEquals(1, getCount(uri1, null, null));
19707d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        assertEquals(1, getCount(uri2, null, null));
19717d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        assertStoredValue(uri1, Groups._ID + "=" + groupId1, null, Groups._ID, groupId1) ;
19727d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        assertStoredValue(uri2, Groups._ID + "=" + groupId2, null, Groups._ID, groupId2) ;
19737d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh    }
19747d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh
19757d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh    public void testGroupInsert() {
19767d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        ContentValues values = new ContentValues();
19774e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov
19787d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.put(Groups.ACCOUNT_NAME, "a");
19794e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.ACCOUNT_TYPE, "b");
19807d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.put(Groups.SOURCE_ID, "c");
19810265a180cf027d149f11f8750652ac67ea08ca24Dmitri Plotnikov        values.put(Groups.VERSION, 42);
19827d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.put(Groups.GROUP_VISIBLE, 1);
19837d9fdcf8346f789436148eff1f00e8f49b370ef0Neel Parekh        values.put(Groups.TITLE, "d");
19844e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.TITLE_RES, 1234);
19854e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.NOTES, "e");
19864e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.RES_PACKAGE, "f");
19874e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.SYSTEM_ID, "g");
19884e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.DELETED, 1);
19894e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.SYNC1, "h");
19904e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.SYNC2, "i");
19914e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.SYNC3, "j");
19924e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.SYNC4, "k");
19934e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov
19944e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        Uri rowUri = mResolver.insert(Groups.CONTENT_URI, values);
19954e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov
19964e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        values.put(Groups.DIRTY, 1);
19974e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        assertStoredValues(rowUri, values);
19984e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov    }
19994e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov
20004e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov    public void testGroupCreationAfterMembershipInsert() {
20014e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        long rawContactId1 = createRawContact(mAccount);
20024e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1");
20034e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov
20044e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        long groupId = assertSingleGroup(NO_LONG, mAccount, "gsid1", null);
20054e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
20064e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov                rawContactId1, groupId, "gsid1");
20074e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov    }
20084e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov
20094e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov    public void testGroupReuseAfterMembershipInsert() {
20104e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        long rawContactId1 = createRawContact(mAccount);
20114e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        long groupId1 = createGroup(mAccount, "gsid1", "title1");
20124e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1");
20134e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov
20144e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        assertSingleGroup(groupId1, mAccount, "gsid1", "title1");
20154e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov        assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
20164e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov                rawContactId1, groupId1, "gsid1");
20174e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov    }
20184e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov
20194e8ced99f8bbb01abd610a6ca60afcabb6ffe737Dmitri Plotnikov    public void testGroupInsertFailureOnGroupIdConflict() {
20201b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        long rawContactId1 = createRawContact(mAccount);
20211b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        long groupId1 = createGroup(mAccount, "gsid1", "title1");
20221b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
20231b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        ContentValues values = new ContentValues();
20241b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        values.put(GroupMembership.RAW_CONTACT_ID, rawContactId1);
20251b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        values.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
20261b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        values.put(GroupMembership.GROUP_SOURCE_ID, "gsid1");
20271b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        values.put(GroupMembership.GROUP_ROW_ID, groupId1);
20281b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        try {
20291b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            mResolver.insert(Data.CONTENT_URI, values);
20301b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            fail("the insert was expected to fail, but it succeeded");
20311b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        } catch (IllegalArgumentException e) {
20321b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov            // this was expected
20331b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        }
20341b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    }
20351b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
20361b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov    public void testGroupSummaryQuery() {
20371b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        final Account account1 = new Account("accountName1", "accountType1");
20381b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        final Account account2 = new Account("accountName2", "accountType2");
20391b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        final long groupId1 = createGroup(account1, "sourceId1", "title1");
20401b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        final long groupId2 = createGroup(account2, "sourceId2", "title2");
20411b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        final long groupId3 = createGroup(account2, "sourceId3", "title3");
20421b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
20431b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // Prepare raw contact id not used at all, to test group summary uri won't be confused
20441b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        // with it.
20451b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        final long rawContactId0 = createRawContactWithName("firstName0", "lastName0");
20461b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
20471b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        final long rawContactId1 = createRawContactWithName("firstName1", "lastName1");
20481b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        insertEmail(rawContactId1, "address1@email.com");
20491b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        insertGroupMembership(rawContactId1, groupId1);
20501b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
20511b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        final long rawContactId2 = createRawContactWithName("firstName2", "lastName2");
20521b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        insertEmail(rawContactId2, "address2@email.com");
20531b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        insertPhoneNumber(rawContactId2, "222-222-2222");
20541b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        insertGroupMembership(rawContactId2, groupId1);
20551b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
20561b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        ContentValues v1 = new ContentValues();
20571b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        v1.put(Groups._ID, groupId1);
20581b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        v1.put(Groups.TITLE, "title1");
20591b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        v1.put(Groups.SOURCE_ID, "sourceId1");
20601b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        v1.put(Groups.ACCOUNT_NAME, account1.name);
20611b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        v1.put(Groups.ACCOUNT_TYPE, account1.type);
20621b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        v1.put(Groups.SUMMARY_COUNT, 2);
20631b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        v1.put(Groups.SUMMARY_WITH_PHONES, 1);
20641b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov
20651b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        ContentValues v2 = new ContentValues();
20661b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        v2.put(Groups._ID, groupId2);
20671b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        v2.put(Groups.TITLE, "title2");
20681b7a7947242bb3b8caaed871775e62d486144c9fDmitri Plotnikov        v2.put(Groups.SOURCE_ID, "sourceId2");
2069ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v2.put(Groups.ACCOUNT_NAME, account2.name);
2070ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v2.put(Groups.ACCOUNT_TYPE, account2.type);
2071ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v2.put(Groups.SUMMARY_COUNT, 0);
2072ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v2.put(Groups.SUMMARY_WITH_PHONES, 0);
2073ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
2074ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        ContentValues v3 = new ContentValues();
2075ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v3.put(Groups._ID, groupId3);
2076ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v3.put(Groups.TITLE, "title3");
2077ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v3.put(Groups.SOURCE_ID, "sourceId3");
2078ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v3.put(Groups.ACCOUNT_NAME, account2.name);
2079ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v3.put(Groups.ACCOUNT_TYPE, account2.type);
2080ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v3.put(Groups.SUMMARY_COUNT, 0);
2081ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v3.put(Groups.SUMMARY_WITH_PHONES, 0);
2082ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
2083ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 });
2084ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
2085ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // Now rawContactId1 has two phone numbers.
2086ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        insertPhoneNumber(rawContactId1, "111-111-1111");
2087ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        insertPhoneNumber(rawContactId1, "111-111-1112");
2088ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // Result should reflect it correctly (don't count phone numbers but raw contacts)
2089ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v1.put(Groups.SUMMARY_WITH_PHONES, v1.getAsInteger(Groups.SUMMARY_WITH_PHONES) + 1);
2090ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 });
2091ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
2092ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // Introduce new raw contact, pretending the user added another info.
2093ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        final long rawContactId3 = createRawContactWithName("firstName3", "lastName3");
2094ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        insertEmail(rawContactId3, "address3@email.com");
2095ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        insertPhoneNumber(rawContactId3, "333-333-3333");
2096ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        insertGroupMembership(rawContactId3, groupId2);
2097ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v2.put(Groups.SUMMARY_COUNT, v2.getAsInteger(Groups.SUMMARY_COUNT) + 1);
2098ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v2.put(Groups.SUMMARY_WITH_PHONES, v2.getAsInteger(Groups.SUMMARY_WITH_PHONES) + 1);
2099ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
2100ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 });
2101ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
2102ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        final Uri uri = Groups.CONTENT_SUMMARY_URI.buildUpon()
2103ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                .appendQueryParameter(Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT, "true")
2104ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov                .build();
2105ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 1);
2106ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v2.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2);
2107ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        v3.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2);
2108ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        assertStoredValues(uri, new ContentValues[] { v1, v2, v3 });
2109ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov
2110ba2c85b4700fbb3ecaf75e1101735f60b5483527Dmitri Plotnikov        // Introduce another group in account1, testing SUMMARY_GROUP_COUNT_PER_ACCOUNT correctly
2111f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        // reflects the change.
2112f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        final long groupId4 = createGroup(account1, "sourceId4", "title4");
2113f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
2114f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT) + 1);
2115f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        ContentValues v4 = new ContentValues();
2116f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        v4.put(Groups._ID, groupId4);
2117f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        v4.put(Groups.TITLE, "title4");
2118f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        v4.put(Groups.SOURCE_ID, "sourceId4");
2119f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        v4.put(Groups.ACCOUNT_NAME, account1.name);
2120f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        v4.put(Groups.ACCOUNT_TYPE, account1.type);
2121f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        v4.put(Groups.SUMMARY_COUNT, 0);
2122f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        v4.put(Groups.SUMMARY_WITH_PHONES, 0);
2123f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        v4.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
2124f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov                v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT));
2125f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        assertStoredValues(uri, new ContentValues[] { v1, v2, v3, v4 });
2126f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
2127f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2128f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    public void testSettingsQuery() {
2129f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        Account account1 = new Account("a", "b");
2130f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        Account account2 = new Account("c", "d");
2131f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        createSettings(account1, "0", "0");
2132f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        createSettings(account2, "1", "1");
2133f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        Uri uri1 = maybeAddAccountQueryParameters(Settings.CONTENT_URI, account1);
2134f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        Uri uri2 = maybeAddAccountQueryParameters(Settings.CONTENT_URI, account2);
2135f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        assertEquals(1, getCount(uri1, null, null));
2136f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        assertEquals(1, getCount(uri2, null, null));
2137f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        assertStoredValue(uri1, Settings.SHOULD_SYNC, "0") ;
2138f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        assertStoredValue(uri1, Settings.UNGROUPED_VISIBLE, "0") ;
2139f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        assertStoredValue(uri2, Settings.SHOULD_SYNC, "1") ;
2140f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        assertStoredValue(uri2, Settings.UNGROUPED_VISIBLE, "1") ;
2141f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov    }
2142e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2143e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    public void testDisplayNameParsingWhenPartsUnspecified() {
2144e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        long rawContactId = createRawContact();
2145e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        ContentValues values = new ContentValues();
2146e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
2147e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        insertStructuredName(rawContactId, values);
2148e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2149e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        assertStructuredName(rawContactId, "Mr.", "John", "Kevin", "von Smith", "Jr.");
2150e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    }
2151e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2152e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    public void testDisplayNameParsingWhenPartsAreNull() {
2153e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        long rawContactId = createRawContact();
2154e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        ContentValues values = new ContentValues();
2155e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
2156e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        values.putNull(StructuredName.GIVEN_NAME);
2157e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        values.putNull(StructuredName.FAMILY_NAME);
2158e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        insertStructuredName(rawContactId, values);
2159e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        assertStructuredName(rawContactId, "Mr.", "John", "Kevin", "von Smith", "Jr.");
2160e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    }
2161e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2162e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    public void testDisplayNameParsingWhenPartsSpecified() {
2163e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        long rawContactId = createRawContact();
2164e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        ContentValues values = new ContentValues();
2165e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
2166e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        values.put(StructuredName.FAMILY_NAME, "Johnson");
2167e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        insertStructuredName(rawContactId, values);
2168e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
2169e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey        assertStructuredName(rawContactId, null, null, null, "Johnson", null);
2170e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey    }
2171e246689441b2ff39cb97de277d6caeec95358863Jeff Sharkey
217209c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov    public void testContactWithoutPhoneticName() {
217309c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        final long rawContactId = createRawContact(null);
217409c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov
217509c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        ContentValues values = new ContentValues();
217609c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        values.put(StructuredName.PREFIX, "Mr");
217709c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        values.put(StructuredName.GIVEN_NAME, "John");
217809c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        values.put(StructuredName.MIDDLE_NAME, "K.");
217909c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        values.put(StructuredName.FAMILY_NAME, "Doe");
218009c6613dd14cb1911da5d62e39a4e54eb8f4666fDmitri Plotnikov        values.put(StructuredName.SUFFIX, "Jr.");
2181f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        Uri dataUri = insertStructuredName(rawContactId, values);
2182f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov
2183f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        values.clear();
2184f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
2185f7f747a00f4fa7a9e564507693419a5a8db0eb8fDmitri Plotnikov        values.put(RawContacts.DISPLAY_NAME_PRIMARY, "Mr John K. Doe, Jr.");
21864a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "Mr Doe, John K., Jr.");
21874a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.putNull(RawContacts.PHONETIC_NAME);
21884a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
21894a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(RawContacts.SORT_KEY_PRIMARY, "John K. Doe, Jr.");
21904a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(RawContacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr.");
21914a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
21924a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
21934a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        assertStoredValues(rawContactUri, values);
21944a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
21954a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.clear();
21964a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
21974a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(Contacts.DISPLAY_NAME_PRIMARY, "Mr John K. Doe, Jr.");
21984a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "Mr Doe, John K., Jr.");
21994a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.putNull(Contacts.PHONETIC_NAME);
2200f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
2201f992bfab334b760d36a053fc0b439382dcfb51adDmitri Plotnikov        values.put(Contacts.SORT_KEY_PRIMARY, "John K. Doe, Jr.");
22024a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(Contacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr.");
220382bd858c9911dfbd8dca52dc276333768b0a429eDmitri Plotnikov
22044a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
22054a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov                queryContactId(rawContactId));
22064a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        assertStoredValues(contactUri, values);
22074a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
22084a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        // The same values should be available through a join with Data
22094a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        assertStoredValues(dataUri, values);
22104a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    }
22114a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
22124a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov    public void testContactWithChineseName() {
22134a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
22144a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        // Only run this test when Chinese collation is supported
22154a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.CHINA)) {
22164a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov            return;
22174a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        }
22184a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
22194a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        long rawContactId = createRawContact(null);
22204a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
22214a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        ContentValues values = new ContentValues();
22224a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(StructuredName.DISPLAY_NAME, "\u6BB5\u5C0F\u6D9B");
22234a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        Uri dataUri = insertStructuredName(rawContactId, values);
22244a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
22254a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.clear();
22264a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
22274a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B");
22284a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
22294a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.putNull(RawContacts.PHONETIC_NAME);
22304a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
22314a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(RawContacts.SORT_KEY_PRIMARY, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
22324a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.put(RawContacts.SORT_KEY_ALTERNATIVE, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
22334a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
22344a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
22354a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        assertStoredValues(rawContactUri, values);
22364a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov
22374a023070dab9a069be4cac5f5ba5554b66238484Dmitri Plotnikov        values.clear();
2238d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
2239d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B");
2240        values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
2241        values.putNull(Contacts.PHONETIC_NAME);
2242        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
2243        values.put(Contacts.SORT_KEY_PRIMARY, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
2244        values.put(Contacts.SORT_KEY_ALTERNATIVE, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
2245
2246        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
2247                queryContactId(rawContactId));
2248        assertStoredValues(contactUri, values);
2249
2250        // The same values should be available through a join with Data
2251        assertStoredValues(dataUri, values);
2252    }
2253
2254    public void testContactWithJapaneseName() {
2255        long rawContactId = createRawContact(null);
2256
2257        ContentValues values = new ContentValues();
2258        values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77");
2259        values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046");
2260        Uri dataUri = insertStructuredName(rawContactId, values);
2261
2262        values.clear();
2263        values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
2264        values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77");
2265        values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77");
2266        values.put(RawContacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046");
2267        values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
2268        values.put(RawContacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046");
2269        values.put(RawContacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046");
2270
2271        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
2272        assertStoredValues(rawContactUri, values);
2273
2274        values.clear();
2275        values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
2276        values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77");
2277        values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77");
2278        values.put(Contacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046");
2279        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
2280        values.put(Contacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046");
2281        values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046");
2282
2283        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
2284                queryContactId(rawContactId));
2285        assertStoredValues(contactUri, values);
2286
2287        // The same values should be available through a join with Data
2288        assertStoredValues(dataUri, values);
2289    }
2290
2291    public void testDisplayNameUpdate() {
2292        long rawContactId1 = createRawContact();
2293        insertEmail(rawContactId1, "potato@acme.com", true);
2294
2295        long rawContactId2 = createRawContact();
2296        insertPhoneNumber(rawContactId2, "123456789", true);
2297
2298        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
2299                rawContactId1, rawContactId2);
2300
2301        assertAggregated(rawContactId1, rawContactId2, "123456789");
2302
2303        insertStructuredName(rawContactId2, "Potato", "Head");
2304
2305        assertAggregated(rawContactId1, rawContactId2, "Potato Head");
2306        assertNetworkNotified(true);
2307    }
2308
2309    public void testDisplayNameFromData() {
2310        long rawContactId = createRawContact();
2311        long contactId = queryContactId(rawContactId);
2312        ContentValues values = new ContentValues();
2313
2314        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2315
2316        assertStoredValue(uri, Contacts.DISPLAY_NAME, null);
2317        insertEmail(rawContactId, "mike@monstersinc.com");
2318        assertStoredValue(uri, Contacts.DISPLAY_NAME, "mike@monstersinc.com");
2319
2320        insertEmail(rawContactId, "james@monstersinc.com", true);
2321        assertStoredValue(uri, Contacts.DISPLAY_NAME, "james@monstersinc.com");
2322
2323        insertPhoneNumber(rawContactId, "1-800-466-4411");
2324        assertStoredValue(uri, Contacts.DISPLAY_NAME, "1-800-466-4411");
2325
2326        // If there are title and company, the company is display name.
2327        values.clear();
2328        values.put(Organization.COMPANY, "Monsters Inc");
2329        Uri organizationUri = insertOrganization(rawContactId, values);
2330        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Monsters Inc");
2331
2332        // If there is nickname, that is display name.
2333        insertNickname(rawContactId, "Sully");
2334        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Sully");
2335
2336        // If there is structured name, that is display name.
2337        values.clear();
2338        values.put(StructuredName.GIVEN_NAME, "James");
2339        values.put(StructuredName.MIDDLE_NAME, "P.");
2340        values.put(StructuredName.FAMILY_NAME, "Sullivan");
2341        insertStructuredName(rawContactId, values);
2342        assertStoredValue(uri, Contacts.DISPLAY_NAME, "James P. Sullivan");
2343    }
2344
2345    public void testDisplayNameFromOrganizationWithoutPhoneticName() {
2346        long rawContactId = createRawContact();
2347        long contactId = queryContactId(rawContactId);
2348        ContentValues values = new ContentValues();
2349
2350        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2351
2352        // If there is title without company, the title is display name.
2353        values.clear();
2354        values.put(Organization.TITLE, "Protagonist");
2355        Uri organizationUri = insertOrganization(rawContactId, values);
2356        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Protagonist");
2357
2358        // If there are title and company, the company is display name.
2359        values.clear();
2360        values.put(Organization.COMPANY, "Monsters Inc");
2361        mResolver.update(organizationUri, values, null, null);
2362
2363        values.clear();
2364        values.put(Contacts.DISPLAY_NAME, "Monsters Inc");
2365        values.putNull(Contacts.PHONETIC_NAME);
2366        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
2367        values.put(Contacts.SORT_KEY_PRIMARY, "Monsters Inc");
2368        values.put(Contacts.SORT_KEY_ALTERNATIVE, "Monsters Inc");
2369        assertStoredValues(uri, values);
2370    }
2371
2372    public void testDisplayNameFromOrganizationWithJapanesePhoneticName() {
2373        long rawContactId = createRawContact();
2374        long contactId = queryContactId(rawContactId);
2375        ContentValues values = new ContentValues();
2376
2377        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2378
2379        // If there is title without company, the title is display name.
2380        values.clear();
2381        values.put(Organization.COMPANY, "DoCoMo");
2382        values.put(Organization.PHONETIC_NAME, "\u30C9\u30B3\u30E2");
2383        Uri organizationUri = insertOrganization(rawContactId, values);
2384
2385        values.clear();
2386        values.put(Contacts.DISPLAY_NAME, "DoCoMo");
2387        values.put(Contacts.PHONETIC_NAME, "\u30C9\u30B3\u30E2");
2388        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
2389        values.put(Contacts.SORT_KEY_PRIMARY, "\u30C9\u30B3\u30E2");
2390        values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u30C9\u30B3\u30E2");
2391        assertStoredValues(uri, values);
2392    }
2393
2394    public void testDisplayNameFromOrganizationWithChineseName() {
2395        boolean hasChineseCollator = false;
2396        final Locale locale[] = Collator.getAvailableLocales();
2397        for (int i = 0; i < locale.length; i++) {
2398            if (locale[i].equals(Locale.CHINA)) {
2399                hasChineseCollator = true;
2400                break;
2401            }
2402        }
2403
2404        if (!hasChineseCollator) {
2405            return;
2406        }
2407
2408        long rawContactId = createRawContact();
2409        long contactId = queryContactId(rawContactId);
2410        ContentValues values = new ContentValues();
2411
2412        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2413
2414        // If there is title without company, the title is display name.
2415        values.clear();
2416        values.put(Organization.COMPANY, "\u4E2D\u56FD\u7535\u4FE1");
2417        Uri organizationUri = insertOrganization(rawContactId, values);
2418
2419        values.clear();
2420        values.put(Contacts.DISPLAY_NAME, "\u4E2D\u56FD\u7535\u4FE1");
2421        values.putNull(Contacts.PHONETIC_NAME);
2422        values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
2423        values.put(Contacts.SORT_KEY_PRIMARY, "ZHONG \u4E2D GUO \u56FD DIAN \u7535 XIN \u4FE1");
2424        values.put(Contacts.SORT_KEY_ALTERNATIVE, "ZHONG \u4E2D GUO \u56FD DIAN \u7535 XIN \u4FE1");
2425        assertStoredValues(uri, values);
2426    }
2427
2428    public void testLookupByOrganization() {
2429        long rawContactId = createRawContact();
2430        long contactId = queryContactId(rawContactId);
2431        ContentValues values = new ContentValues();
2432
2433        values.clear();
2434        values.put(Organization.COMPANY, "acmecorp");
2435        values.put(Organization.TITLE, "president");
2436        Uri organizationUri = insertOrganization(rawContactId, values);
2437
2438        assertContactFilter(contactId, "acmecorp");
2439        assertContactFilter(contactId, "president");
2440
2441        values.clear();
2442        values.put(Organization.DEPARTMENT, "software");
2443        mResolver.update(organizationUri, values, null, null);
2444
2445        assertContactFilter(contactId, "acmecorp");
2446        assertContactFilter(contactId, "president");
2447
2448        values.clear();
2449        values.put(Organization.COMPANY, "incredibles");
2450        mResolver.update(organizationUri, values, null, null);
2451
2452        assertContactFilter(contactId, "incredibles");
2453        assertContactFilter(contactId, "president");
2454
2455        values.clear();
2456        values.put(Organization.TITLE, "director");
2457        mResolver.update(organizationUri, values, null, null);
2458
2459        assertContactFilter(contactId, "incredibles");
2460        assertContactFilter(contactId, "director");
2461
2462        values.clear();
2463        values.put(Organization.COMPANY, "monsters");
2464        values.put(Organization.TITLE, "scarer");
2465        mResolver.update(organizationUri, values, null, null);
2466
2467        assertContactFilter(contactId, "monsters");
2468        assertContactFilter(contactId, "scarer");
2469    }
2470
2471    private void assertContactFilter(long contactId, String filter) {
2472        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
2473        assertStoredValue(filterUri, Contacts._ID, contactId);
2474    }
2475
2476    private void assertContactFilterNoResult(String filter) {
2477        Uri filterUri4 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, filter);
2478        assertEquals(0, getCount(filterUri4, null, null));
2479    }
2480
2481    public void testSearchSnippetOrganization() throws Exception {
2482        long rawContactId = createRawContactWithName();
2483        long contactId = queryContactId(rawContactId);
2484
2485        // Some random data element
2486        insertEmail(rawContactId, "inc@corp.com");
2487
2488        ContentValues values = new ContentValues();
2489        values.clear();
2490        values.put(Organization.COMPANY, "acmecorp");
2491        values.put(Organization.TITLE, "engineer");
2492        Uri organizationUri = insertOrganization(rawContactId, values);
2493
2494        // Add another matching organization
2495        values.put(Organization.COMPANY, "acmeinc");
2496        insertOrganization(rawContactId, values);
2497
2498        // Add another non-matching organization
2499        values.put(Organization.COMPANY, "corpacme");
2500        insertOrganization(rawContactId, values);
2501
2502        // And another data element
2503        insertEmail(rawContactId, "emca@corp.com", true, Email.TYPE_CUSTOM, "Custom");
2504
2505        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("acme"));
2506
2507        values.clear();
2508        values.put(Contacts._ID, contactId);
2509        values.put(SearchSnippetColumns.SNIPPET, "engineer, [acmecorp]");
2510        assertStoredValues(filterUri, values);
2511    }
2512
2513    public void testSearchSnippetEmail() throws Exception {
2514        long rawContactId = createRawContact();
2515        long contactId = queryContactId(rawContactId);
2516        ContentValues values = new ContentValues();
2517
2518        insertStructuredName(rawContactId, "John", "Doe");
2519        Uri dataUri = insertEmail(rawContactId, "acme@corp.com", true, Email.TYPE_CUSTOM, "Custom");
2520
2521        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("acme"));
2522
2523        values.clear();
2524        values.put(Contacts._ID, contactId);
2525        values.put(SearchSnippetColumns.SNIPPET, "[acme@corp.com]");
2526        assertStoredValues(filterUri, values);
2527    }
2528
2529    public void testSearchSnippetPhone() throws Exception {
2530        long rawContactId = createRawContact();
2531        long contactId = queryContactId(rawContactId);
2532        ContentValues values = new ContentValues();
2533
2534        insertStructuredName(rawContactId, "Cave", "Johnson");
2535        insertPhoneNumber(rawContactId, "(860) 555-1234");
2536
2537        values.clear();
2538        values.put(Contacts._ID, contactId);
2539        values.put(SearchSnippetColumns.SNIPPET, "[(860) 555-1234]");
2540
2541        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
2542                Uri.encode("86 (0) 5-55-12-34")), values);
2543        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
2544                Uri.encode("860 555-1234")), values);
2545        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
2546                Uri.encode("860")), values);
2547        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
2548                Uri.encode("8605551234")), values);
2549        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
2550                Uri.encode("860555")), values);
2551        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
2552                Uri.encode("860 555")), values);
2553        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
2554                Uri.encode("860-555")), values);
2555    }
2556
2557    public void testSearchSnippetNickname() throws Exception {
2558        long rawContactId = createRawContactWithName();
2559        long contactId = queryContactId(rawContactId);
2560        ContentValues values = new ContentValues();
2561
2562        Uri dataUri = insertNickname(rawContactId, "Incredible");
2563
2564        Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("inc"));
2565
2566        values.clear();
2567        values.put(Contacts._ID, contactId);
2568        values.put(SearchSnippetColumns.SNIPPET, "[Incredible]");
2569        assertStoredValues(filterUri, values);
2570    }
2571
2572    public void testSearchSnippetEmptyForNameInDisplayName() throws Exception {
2573        long rawContactId = createRawContact();
2574        long contactId = queryContactId(rawContactId);
2575        insertStructuredName(rawContactId, "Cave", "Johnson");
2576        insertEmail(rawContactId, "cave@aperturescience.com", true);
2577
2578        ContentValues emptySnippet = new ContentValues();
2579        emptySnippet.clear();
2580        emptySnippet.put(Contacts._ID, contactId);
2581        emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null);
2582
2583        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("cave")),
2584                emptySnippet);
2585        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("john")),
2586                emptySnippet);
2587    }
2588
2589    public void testSearchSnippetEmptyForNicknameInDisplayName() throws Exception {
2590        long rawContactId = createRawContact();
2591        long contactId = queryContactId(rawContactId);
2592        insertNickname(rawContactId, "Caveman");
2593        insertEmail(rawContactId, "cave@aperturescience.com", true);
2594
2595        ContentValues emptySnippet = new ContentValues();
2596        emptySnippet.clear();
2597        emptySnippet.put(Contacts._ID, contactId);
2598        emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null);
2599
2600        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("cave")),
2601                emptySnippet);
2602    }
2603
2604    public void testSearchSnippetEmptyForCompanyInDisplayName() throws Exception {
2605        long rawContactId = createRawContact();
2606        long contactId = queryContactId(rawContactId);
2607        ContentValues company = new ContentValues();
2608        company.clear();
2609        company.put(Organization.COMPANY, "Aperture Science");
2610        company.put(Organization.TITLE, "President");
2611        insertOrganization(rawContactId, company);
2612        insertEmail(rawContactId, "aperturepresident@aperturescience.com", true);
2613
2614        ContentValues emptySnippet = new ContentValues();
2615        emptySnippet.clear();
2616        emptySnippet.put(Contacts._ID, contactId);
2617        emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null);
2618
2619        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
2620                Uri.encode("aperture")), emptySnippet);
2621    }
2622
2623    public void testSearchSnippetEmptyForPhoneInDisplayName() throws Exception {
2624        long rawContactId = createRawContact();
2625        long contactId = queryContactId(rawContactId);
2626        insertPhoneNumber(rawContactId, "860-555-1234");
2627        insertEmail(rawContactId, "860@aperturescience.com", true);
2628
2629        ContentValues emptySnippet = new ContentValues();
2630        emptySnippet.clear();
2631        emptySnippet.put(Contacts._ID, contactId);
2632        emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null);
2633
2634        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("860")),
2635                emptySnippet);
2636    }
2637
2638    public void testSearchSnippetEmptyForEmailInDisplayName() throws Exception {
2639        long rawContactId = createRawContact();
2640        long contactId = queryContactId(rawContactId);
2641        insertEmail(rawContactId, "cave@aperturescience.com", true);
2642        insertNote(rawContactId, "Cave Johnson is president of Aperture Science");
2643
2644        ContentValues emptySnippet = new ContentValues();
2645        emptySnippet.clear();
2646        emptySnippet.put(Contacts._ID, contactId);
2647        emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null);
2648
2649        assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode("cave")),
2650                emptySnippet);
2651    }
2652
2653    public void testDisplayNameUpdateFromStructuredNameUpdate() {
2654        long rawContactId = createRawContact();
2655        Uri nameUri = insertStructuredName(rawContactId, "Slinky", "Dog");
2656
2657        long contactId = queryContactId(rawContactId);
2658
2659        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2660        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky Dog");
2661
2662        ContentValues values = new ContentValues();
2663        values.putNull(StructuredName.FAMILY_NAME);
2664
2665        mResolver.update(nameUri, values, null, null);
2666        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky");
2667
2668        values.putNull(StructuredName.GIVEN_NAME);
2669
2670        mResolver.update(nameUri, values, null, null);
2671        assertStoredValue(uri, Contacts.DISPLAY_NAME, null);
2672
2673        values.put(StructuredName.FAMILY_NAME, "Dog");
2674        mResolver.update(nameUri, values, null, null);
2675
2676        assertStoredValue(uri, Contacts.DISPLAY_NAME, "Dog");
2677    }
2678
2679    public void testInsertDataWithContentProviderOperations() throws Exception {
2680        ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
2681                .withValues(new ContentValues())
2682                .build();
2683        ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI)
2684                .withValueBackReference(Data.RAW_CONTACT_ID, 0)
2685                .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
2686                .withValue(StructuredName.GIVEN_NAME, "John")
2687                .withValue(StructuredName.FAMILY_NAME, "Doe")
2688                .build();
2689        ContentProviderResult[] results =
2690                mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(cpo1, cpo2));
2691        long contactId = queryContactId(ContentUris.parseId(results[0].uri));
2692        Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2693        assertStoredValue(uri, Contacts.DISPLAY_NAME, "John Doe");
2694    }
2695
2696    public void testSendToVoicemailDefault() {
2697        long rawContactId = createRawContactWithName();
2698        long contactId = queryContactId(rawContactId);
2699
2700        Cursor c = queryContact(contactId);
2701        assertTrue(c.moveToNext());
2702        int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL));
2703        assertEquals(0, sendToVoicemail);
2704        c.close();
2705    }
2706
2707    public void testSetSendToVoicemailAndRingtone() {
2708        long rawContactId = createRawContactWithName();
2709        long contactId = queryContactId(rawContactId);
2710
2711        updateSendToVoicemailAndRingtone(contactId, true, "foo");
2712        assertSendToVoicemailAndRingtone(contactId, true, "foo");
2713        assertNetworkNotified(false);
2714
2715        updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar");
2716        assertSendToVoicemailAndRingtone(contactId, false, "bar");
2717        assertNetworkNotified(false);
2718    }
2719
2720    public void testSendToVoicemailAndRingtoneAfterAggregation() {
2721        long rawContactId1 = createRawContactWithName("a", "b");
2722        long contactId1 = queryContactId(rawContactId1);
2723        updateSendToVoicemailAndRingtone(contactId1, true, "foo");
2724
2725        long rawContactId2 = createRawContactWithName("c", "d");
2726        long contactId2 = queryContactId(rawContactId2);
2727        updateSendToVoicemailAndRingtone(contactId2, true, "bar");
2728
2729        // Aggregate them
2730        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
2731                rawContactId1, rawContactId2);
2732
2733        // Both contacts had "send to VM", the contact now has the same value
2734        assertSendToVoicemailAndRingtone(contactId1, true, "foo,bar"); // Either foo or bar
2735    }
2736
2737    public void testDoNotSendToVoicemailAfterAggregation() {
2738        long rawContactId1 = createRawContactWithName("e", "f");
2739        long contactId1 = queryContactId(rawContactId1);
2740        updateSendToVoicemailAndRingtone(contactId1, true, null);
2741
2742        long rawContactId2 = createRawContactWithName("g", "h");
2743        long contactId2 = queryContactId(rawContactId2);
2744        updateSendToVoicemailAndRingtone(contactId2, false, null);
2745
2746        // Aggregate them
2747        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
2748                rawContactId1, rawContactId2);
2749
2750        // Since one of the contacts had "don't send to VM" that setting wins for the aggregate
2751        assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), false, null);
2752    }
2753
2754    public void testSetSendToVoicemailAndRingtonePreservedAfterJoinAndSplit() {
2755        long rawContactId1 = createRawContactWithName("i", "j");
2756        long contactId1 = queryContactId(rawContactId1);
2757        updateSendToVoicemailAndRingtone(contactId1, true, "foo");
2758
2759        long rawContactId2 = createRawContactWithName("k", "l");
2760        long contactId2 = queryContactId(rawContactId2);
2761        updateSendToVoicemailAndRingtone(contactId2, false, "bar");
2762
2763        // Aggregate them
2764        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
2765                rawContactId1, rawContactId2);
2766
2767        // Split them
2768        setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
2769                rawContactId1, rawContactId2);
2770
2771        assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), true, "foo");
2772        assertSendToVoicemailAndRingtone(queryContactId(rawContactId2), false, "bar");
2773    }
2774
2775    public void testStatusUpdateInsert() {
2776        long rawContactId = createRawContact();
2777        Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
2778        long dataId = ContentUris.parseId(imUri);
2779
2780        ContentValues values = new ContentValues();
2781        values.put(StatusUpdates.DATA_ID, dataId);
2782        values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM);
2783        values.putNull(StatusUpdates.CUSTOM_PROTOCOL);
2784        values.put(StatusUpdates.IM_HANDLE, "aim");
2785        values.put(StatusUpdates.PRESENCE, StatusUpdates.INVISIBLE);
2786        values.put(StatusUpdates.STATUS, "Hiding");
2787        values.put(StatusUpdates.STATUS_TIMESTAMP, 100);
2788        values.put(StatusUpdates.STATUS_RES_PACKAGE, "a.b.c");
2789        values.put(StatusUpdates.STATUS_ICON, 1234);
2790        values.put(StatusUpdates.STATUS_LABEL, 2345);
2791
2792        Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values);
2793
2794        assertStoredValues(resultUri, values);
2795
2796        long contactId = queryContactId(rawContactId);
2797        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2798
2799        values.clear();
2800        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
2801        values.put(Contacts.CONTACT_STATUS, "Hiding");
2802        values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100);
2803        values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "a.b.c");
2804        values.put(Contacts.CONTACT_STATUS_ICON, 1234);
2805        values.put(Contacts.CONTACT_STATUS_LABEL, 2345);
2806
2807        assertStoredValues(contactUri, values);
2808
2809        values.clear();
2810        values.put(StatusUpdates.DATA_ID, dataId);
2811        values.put(StatusUpdates.STATUS, "Cloaked");
2812        values.put(StatusUpdates.STATUS_TIMESTAMP, 200);
2813        values.put(StatusUpdates.STATUS_RES_PACKAGE, "d.e.f");
2814        values.put(StatusUpdates.STATUS_ICON, 4321);
2815        values.put(StatusUpdates.STATUS_LABEL, 5432);
2816        mResolver.insert(StatusUpdates.CONTENT_URI, values);
2817
2818        values.clear();
2819        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
2820        values.put(Contacts.CONTACT_STATUS, "Cloaked");
2821        values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 200);
2822        values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "d.e.f");
2823        values.put(Contacts.CONTACT_STATUS_ICON, 4321);
2824        values.put(Contacts.CONTACT_STATUS_LABEL, 5432);
2825        assertStoredValues(contactUri, values);
2826    }
2827
2828    public void testStatusUpdateInferAttribution() {
2829        long rawContactId = createRawContact();
2830        Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
2831        long dataId = ContentUris.parseId(imUri);
2832
2833        ContentValues values = new ContentValues();
2834        values.put(StatusUpdates.DATA_ID, dataId);
2835        values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM);
2836        values.put(StatusUpdates.IM_HANDLE, "aim");
2837        values.put(StatusUpdates.STATUS, "Hiding");
2838
2839        Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values);
2840
2841        values.clear();
2842        values.put(StatusUpdates.DATA_ID, dataId);
2843        values.put(StatusUpdates.STATUS_LABEL, com.android.internal.R.string.imProtocolAim);
2844        values.put(StatusUpdates.STATUS, "Hiding");
2845
2846        assertStoredValues(resultUri, values);
2847    }
2848
2849    public void testStatusUpdateMatchingImOrEmail() {
2850        long rawContactId = createRawContact();
2851        insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
2852        insertImHandle(rawContactId, Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im");
2853        insertEmail(rawContactId, "m@acme.com");
2854
2855        // Match on IM (standard)
2856        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
2857                StatusUpdates.CAPABILITY_HAS_CAMERA);
2858
2859        // Match on IM (custom)
2860        insertStatusUpdate(Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im", StatusUpdates.IDLE, "Idle",
2861                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
2862
2863        // Match on Email
2864        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "m@acme.com", StatusUpdates.AWAY, "Away",
2865                StatusUpdates.CAPABILITY_HAS_VOICE);
2866
2867        // No match
2868        insertStatusUpdate(Im.PROTOCOL_ICQ, null, "12345", StatusUpdates.DO_NOT_DISTURB, "Go away",
2869                StatusUpdates.CAPABILITY_HAS_CAMERA);
2870
2871        Cursor c = mResolver.query(StatusUpdates.CONTENT_URI, new String[] {
2872                StatusUpdates.DATA_ID, StatusUpdates.PROTOCOL, StatusUpdates.CUSTOM_PROTOCOL,
2873                StatusUpdates.PRESENCE, StatusUpdates.STATUS},
2874                PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null, StatusUpdates.DATA_ID);
2875        assertTrue(c.moveToNext());
2876        assertStatusUpdate(c, Im.PROTOCOL_AIM, null, StatusUpdates.AVAILABLE, "Available");
2877        assertTrue(c.moveToNext());
2878        assertStatusUpdate(c, Im.PROTOCOL_CUSTOM, "my_im_proto", StatusUpdates.IDLE, "Idle");
2879        assertTrue(c.moveToNext());
2880        assertStatusUpdate(c, Im.PROTOCOL_GOOGLE_TALK, null, StatusUpdates.AWAY, "Away");
2881        assertFalse(c.moveToNext());
2882        c.close();
2883
2884        long contactId = queryContactId(rawContactId);
2885        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2886
2887        ContentValues values = new ContentValues();
2888        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
2889        values.put(Contacts.CONTACT_STATUS, "Available");
2890        assertStoredValuesWithProjection(contactUri, values);
2891    }
2892
2893    public void testStatusUpdateUpdateAndDelete() {
2894        long rawContactId = createRawContact();
2895        insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
2896
2897        long contactId = queryContactId(rawContactId);
2898        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2899
2900        ContentValues values = new ContentValues();
2901        values.putNull(Contacts.CONTACT_PRESENCE);
2902        values.putNull(Contacts.CONTACT_STATUS);
2903        assertStoredValuesWithProjection(contactUri, values);
2904
2905        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AWAY, "BUSY",
2906                StatusUpdates.CAPABILITY_HAS_CAMERA);
2907        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.DO_NOT_DISTURB, "GO AWAY",
2908                StatusUpdates.CAPABILITY_HAS_CAMERA);
2909        Uri statusUri =
2910            insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
2911                    StatusUpdates.CAPABILITY_HAS_CAMERA);
2912        long statusId = ContentUris.parseId(statusUri);
2913
2914        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
2915        values.put(Contacts.CONTACT_STATUS, "Available");
2916        assertStoredValuesWithProjection(contactUri, values);
2917
2918        // update status_updates table to set new values for
2919        //     status_updates.status
2920        //     status_updates.status_ts
2921        //     presence
2922        long updatedTs = 200;
2923        String testUpdate = "test_update";
2924        String selection = StatusUpdates.DATA_ID + "=" + statusId;
2925        values.clear();
2926        values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
2927        values.put(StatusUpdates.STATUS, testUpdate);
2928        values.put(StatusUpdates.PRESENCE, "presence_test");
2929        mResolver.update(StatusUpdates.CONTENT_URI, values,
2930                StatusUpdates.DATA_ID + "=" + statusId, null);
2931        assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
2932
2933        // update status_updates table to set new values for columns in status_updates table ONLY
2934        // i.e., no rows in presence table are to be updated.
2935        updatedTs = 300;
2936        testUpdate = "test_update_new";
2937        selection = StatusUpdates.DATA_ID + "=" + statusId;
2938        values.clear();
2939        values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
2940        values.put(StatusUpdates.STATUS, testUpdate);
2941        mResolver.update(StatusUpdates.CONTENT_URI, values,
2942                StatusUpdates.DATA_ID + "=" + statusId, null);
2943        // make sure the presence column value is still the old value
2944        values.put(StatusUpdates.PRESENCE, "presence_test");
2945        assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
2946
2947        // update status_updates table to set new values for columns in presence table ONLY
2948        // i.e., no rows in status_updates table are to be updated.
2949        selection = StatusUpdates.DATA_ID + "=" + statusId;
2950        values.clear();
2951        values.put(StatusUpdates.PRESENCE, "presence_test_new");
2952        mResolver.update(StatusUpdates.CONTENT_URI, values,
2953                StatusUpdates.DATA_ID + "=" + statusId, null);
2954        // make sure the status_updates table is not updated
2955        values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
2956        values.put(StatusUpdates.STATUS, testUpdate);
2957        assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
2958
2959        // effect "delete status_updates" operation and expect the following
2960        //   data deleted from status_updates table
2961        //   presence set to null
2962        mResolver.delete(StatusUpdates.CONTENT_URI, StatusUpdates.DATA_ID + "=" + statusId, null);
2963        values.clear();
2964        values.putNull(Contacts.CONTACT_PRESENCE);
2965        assertStoredValuesWithProjection(contactUri, values);
2966    }
2967
2968    public void testStatusUpdateUpdateToNull() {
2969        long rawContactId = createRawContact();
2970        insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
2971
2972        long contactId = queryContactId(rawContactId);
2973        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
2974
2975        ContentValues values = new ContentValues();
2976        Uri statusUri =
2977            insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
2978                    StatusUpdates.CAPABILITY_HAS_CAMERA);
2979        long statusId = ContentUris.parseId(statusUri);
2980
2981        values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
2982        values.put(Contacts.CONTACT_STATUS, "Available");
2983        assertStoredValuesWithProjection(contactUri, values);
2984
2985        values.clear();
2986        values.putNull(StatusUpdates.PRESENCE);
2987        mResolver.update(StatusUpdates.CONTENT_URI, values,
2988                StatusUpdates.DATA_ID + "=" + statusId, null);
2989
2990        values.clear();
2991        values.putNull(Contacts.CONTACT_PRESENCE);
2992        values.put(Contacts.CONTACT_STATUS, "Available");
2993        assertStoredValuesWithProjection(contactUri, values);
2994    }
2995
2996    public void testStatusUpdateWithTimestamp() {
2997        long rawContactId = createRawContact();
2998        insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
2999        insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
3000
3001        long contactId = queryContactId(rawContactId);
3002        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
3003        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Offline", 80,
3004                StatusUpdates.CAPABILITY_HAS_CAMERA);
3005        insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Available", 100,
3006                StatusUpdates.CAPABILITY_HAS_CAMERA);
3007        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", 0, "Busy", 90,
3008                StatusUpdates.CAPABILITY_HAS_CAMERA);
3009
3010        // Should return the latest status
3011        ContentValues values = new ContentValues();
3012        values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100);
3013        values.put(Contacts.CONTACT_STATUS, "Available");
3014        assertStoredValuesWithProjection(contactUri, values);
3015    }
3016
3017    private void assertStatusUpdate(Cursor c, int protocol, String customProtocol, int presence,
3018            String status) {
3019        ContentValues values = new ContentValues();
3020        values.put(StatusUpdates.PROTOCOL, protocol);
3021        values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
3022        values.put(StatusUpdates.PRESENCE, presence);
3023        values.put(StatusUpdates.STATUS, status);
3024        assertCursorValues(c, values);
3025    }
3026
3027    // Stream item query test cases.
3028
3029    public void testQueryStreamItemsByRawContactId() {
3030        long rawContactId = createRawContact(mAccount);
3031        ContentValues values = buildGenericStreamItemValues();
3032        insertStreamItem(rawContactId, values, mAccount);
3033        assertStoredValues(
3034                Uri.withAppendedPath(
3035                        ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
3036                        RawContacts.StreamItems.CONTENT_DIRECTORY),
3037                values);
3038    }
3039
3040    public void testQueryStreamItemsByContactId() {
3041        long rawContactId = createRawContact();
3042        long contactId = queryContactId(rawContactId);
3043        ContentValues values = buildGenericStreamItemValues();
3044        insertStreamItem(rawContactId, values, null);
3045        assertStoredValues(
3046                Uri.withAppendedPath(
3047                        ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
3048                        Contacts.StreamItems.CONTENT_DIRECTORY),
3049                values);
3050    }
3051
3052    public void testQueryStreamItemsByLookupKey() {
3053        long rawContactId = createRawContact();
3054        long contactId = queryContactId(rawContactId);
3055        String lookupKey = queryLookupKey(contactId);
3056        ContentValues values = buildGenericStreamItemValues();
3057        insertStreamItem(rawContactId, values, null);
3058        assertStoredValues(
3059                Uri.withAppendedPath(
3060                        Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
3061                        Contacts.StreamItems.CONTENT_DIRECTORY),
3062                values);
3063    }
3064
3065    public void testQueryStreamItemsByLookupKeyAndContactId() {
3066        long rawContactId = createRawContact();
3067        long contactId = queryContactId(rawContactId);
3068        String lookupKey = queryLookupKey(contactId);
3069        ContentValues values = buildGenericStreamItemValues();
3070        insertStreamItem(rawContactId, values, null);
3071        assertStoredValues(
3072                Uri.withAppendedPath(
3073                        ContentUris.withAppendedId(
3074                                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
3075                                contactId),
3076                        Contacts.StreamItems.CONTENT_DIRECTORY),
3077                values);
3078    }
3079
3080    public void testQueryStreamItems() {
3081        long rawContactId = createRawContact();
3082        ContentValues values = buildGenericStreamItemValues();
3083        insertStreamItem(rawContactId, values, null);
3084        assertStoredValues(StreamItems.CONTENT_URI, values);
3085    }
3086
3087    public void testQueryStreamItemsWithSelection() {
3088        long rawContactId = createRawContact();
3089        ContentValues firstValues = buildGenericStreamItemValues();
3090        insertStreamItem(rawContactId, firstValues, null);
3091
3092        ContentValues secondValues = buildGenericStreamItemValues();
3093        secondValues.put(StreamItems.TEXT, "Goodbye world");
3094        insertStreamItem(rawContactId, secondValues, null);
3095
3096        // Select only the first stream item.
3097        assertStoredValues(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?",
3098                new String[]{"Hello world"}, firstValues);
3099
3100        // Select only the second stream item.
3101        assertStoredValues(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?",
3102                new String[]{"Goodbye world"}, secondValues);
3103    }
3104
3105    public void testQueryStreamItemById() {
3106        long rawContactId = createRawContact();
3107        ContentValues firstValues = buildGenericStreamItemValues();
3108        Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
3109        long firstStreamItemId = ContentUris.parseId(resultUri);
3110
3111        ContentValues secondValues = buildGenericStreamItemValues();
3112        secondValues.put(StreamItems.TEXT, "Goodbye world");
3113        resultUri = insertStreamItem(rawContactId, secondValues, null);
3114        long secondStreamItemId = ContentUris.parseId(resultUri);
3115
3116        // Select only the first stream item.
3117        assertStoredValues(ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId),
3118                firstValues);
3119
3120        // Select only the second stream item.
3121        assertStoredValues(ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId),
3122                secondValues);
3123    }
3124
3125    // Stream item photo insertion + query test cases.
3126
3127    public void testQueryStreamItemPhotoWithSelection() {
3128        long rawContactId = createRawContact();
3129        ContentValues values = buildGenericStreamItemValues();
3130        Uri resultUri = insertStreamItem(rawContactId, values, null);
3131        long streamItemId = ContentUris.parseId(resultUri);
3132
3133        ContentValues photo1Values = buildGenericStreamItemPhotoValues(1);
3134        insertStreamItemPhoto(streamItemId, photo1Values, null);
3135        photo1Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
3136        ContentValues photo2Values = buildGenericStreamItemPhotoValues(2);
3137        insertStreamItemPhoto(streamItemId, photo2Values, null);
3138
3139        // Select only the first photo.
3140        assertStoredValues(StreamItems.CONTENT_PHOTO_URI, StreamItemPhotos.SORT_INDEX + "=?",
3141                new String[]{"1"}, photo1Values);
3142    }
3143
3144    public void testQueryStreamItemPhotoByStreamItemId() {
3145        long rawContactId = createRawContact();
3146
3147        // Insert a first stream item.
3148        ContentValues firstValues = buildGenericStreamItemValues();
3149        Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
3150        long firstStreamItemId = ContentUris.parseId(resultUri);
3151
3152        // Insert a second stream item.
3153        ContentValues secondValues = buildGenericStreamItemValues();
3154        resultUri = insertStreamItem(rawContactId, secondValues, null);
3155        long secondStreamItemId = ContentUris.parseId(resultUri);
3156
3157        // Add a photo to the first stream item.
3158        ContentValues photo1Values = buildGenericStreamItemPhotoValues(1);
3159        insertStreamItemPhoto(firstStreamItemId, photo1Values, null);
3160        photo1Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
3161
3162        // Add a photo to the second stream item.
3163        ContentValues photo2Values = buildGenericStreamItemPhotoValues(1);
3164        photo2Values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
3165                R.drawable.nebula, PhotoSize.ORIGINAL));
3166        insertStreamItemPhoto(secondStreamItemId, photo2Values, null);
3167        photo2Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
3168
3169        // Select only the photos from the second stream item.
3170        assertStoredValues(Uri.withAppendedPath(
3171                ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId),
3172                StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), photo2Values);
3173    }
3174
3175    public void testQueryStreamItemPhotoByStreamItemPhotoId() {
3176        long rawContactId = createRawContact();
3177
3178        // Insert a first stream item.
3179        ContentValues firstValues = buildGenericStreamItemValues();
3180        Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
3181        long firstStreamItemId = ContentUris.parseId(resultUri);
3182
3183        // Insert a second stream item.
3184        ContentValues secondValues = buildGenericStreamItemValues();
3185        resultUri = insertStreamItem(rawContactId, secondValues, null);
3186        long secondStreamItemId = ContentUris.parseId(resultUri);
3187
3188        // Add a photo to the first stream item.
3189        ContentValues photo1Values = buildGenericStreamItemPhotoValues(1);
3190        resultUri = insertStreamItemPhoto(firstStreamItemId, photo1Values, null);
3191        long firstPhotoId = ContentUris.parseId(resultUri);
3192        photo1Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
3193
3194        // Add a photo to the second stream item.
3195        ContentValues photo2Values = buildGenericStreamItemPhotoValues(1);
3196        photo2Values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
3197                R.drawable.galaxy, PhotoSize.ORIGINAL));
3198        resultUri = insertStreamItemPhoto(secondStreamItemId, photo2Values, null);
3199        long secondPhotoId = ContentUris.parseId(resultUri);
3200        photo2Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
3201
3202        // Select the first photo.
3203        assertStoredValues(ContentUris.withAppendedId(
3204                Uri.withAppendedPath(
3205                        ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId),
3206                        StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
3207                firstPhotoId),
3208                photo1Values);
3209
3210        // Select the second photo.
3211        assertStoredValues(ContentUris.withAppendedId(
3212                Uri.withAppendedPath(
3213                        ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId),
3214                        StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
3215                secondPhotoId),
3216                photo2Values);
3217    }
3218
3219    // Stream item insertion test cases.
3220
3221    public void testInsertStreamItemIntoOtherAccount() {
3222        long rawContactId = createRawContact(mAccount);
3223        ContentValues values = buildGenericStreamItemValues();
3224        try {
3225            insertStreamItem(rawContactId, values, mAccountTwo);
3226            fail("Stream insertion was allowed in another account's raw contact.");
3227        } catch (SecurityException expected) {
3228            // Trying to insert stream items into account one's raw contact is forbidden.
3229        }
3230    }
3231
3232    public void testInsertStreamItemInProfileRequiresWriteProfileAccess() {
3233        long profileRawContactId = createBasicProfileContact(new ContentValues());
3234
3235        // With our (default) write profile permission, we should be able to insert a stream item.
3236        ContentValues values = buildGenericStreamItemValues();
3237        insertStreamItem(profileRawContactId, values, null);
3238
3239        // Now take away write profile permission.
3240        mActor.removePermissions("android.permission.WRITE_PROFILE");
3241
3242        // Try inserting another stream item.
3243        try {
3244            insertStreamItem(profileRawContactId, values, null);
3245            fail("Should require WRITE_PROFILE access to insert a stream item in the profile.");
3246        } catch (SecurityException expected) {
3247            // Trying to insert a stream item in the profile without WRITE_PROFILE permission
3248            // should fail.
3249        }
3250    }
3251
3252    public void testInsertStreamItemWithContentValues() {
3253        long rawContactId = createRawContact();
3254        ContentValues values = buildGenericStreamItemValues();
3255        values.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3256        mResolver.insert(StreamItems.CONTENT_URI, values);
3257        assertStoredValues(Uri.withAppendedPath(
3258                ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
3259                RawContacts.StreamItems.CONTENT_DIRECTORY), values);
3260    }
3261
3262    public void testInsertStreamItemOverLimit() {
3263        long rawContactId = createRawContact();
3264        ContentValues values = buildGenericStreamItemValues();
3265        values.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3266
3267        List<Long> streamItemIds = Lists.newArrayList();
3268
3269        // Insert MAX + 1 stream items.
3270        long baseTime = System.currentTimeMillis();
3271        for (int i = 0; i < 6; i++) {
3272            values.put(StreamItems.TIMESTAMP, baseTime + i);
3273            Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values);
3274            streamItemIds.add(ContentUris.parseId(resultUri));
3275        }
3276        Long doomedStreamItemId = streamItemIds.get(0);
3277
3278        // There should only be MAX items.  The oldest one should have been cleaned up.
3279        Cursor c = mResolver.query(
3280                Uri.withAppendedPath(
3281                        ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
3282                        RawContacts.StreamItems.CONTENT_DIRECTORY),
3283                new String[]{StreamItems._ID}, null, null, null);
3284        try {
3285            while(c.moveToNext()) {
3286                long streamItemId = c.getLong(0);
3287                streamItemIds.remove(streamItemId);
3288            }
3289        } finally {
3290            c.close();
3291        }
3292
3293        assertEquals(1, streamItemIds.size());
3294        assertEquals(doomedStreamItemId, streamItemIds.get(0));
3295    }
3296
3297    public void testInsertStreamItemOlderThanOldestInLimit() {
3298        long rawContactId = createRawContact();
3299        ContentValues values = buildGenericStreamItemValues();
3300        values.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3301
3302        // Insert MAX stream items.
3303        long baseTime = System.currentTimeMillis();
3304        for (int i = 0; i < 5; i++) {
3305            values.put(StreamItems.TIMESTAMP, baseTime + i);
3306            Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values);
3307            assertNotSame("Expected non-0 stream item ID to be inserted",
3308                    0L, ContentUris.parseId(resultUri));
3309        }
3310
3311        // Now try to insert a stream item that's older.  It should be deleted immediately
3312        // and return an ID of 0.
3313        values.put(StreamItems.TIMESTAMP, baseTime - 1);
3314        Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values);
3315        assertEquals(0L, ContentUris.parseId(resultUri));
3316    }
3317
3318    // Stream item photo insertion test cases.
3319
3320    public void testInsertStreamItemsAndPhotosInBatch() throws Exception {
3321        long rawContactId = createRawContact();
3322        ContentValues streamItemValues = buildGenericStreamItemValues();
3323        ContentValues streamItemPhotoValues = buildGenericStreamItemPhotoValues(0);
3324
3325        ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
3326        ops.add(ContentProviderOperation.newInsert(
3327                Uri.withAppendedPath(
3328                        ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
3329                        RawContacts.StreamItems.CONTENT_DIRECTORY))
3330                .withValues(streamItemValues).build());
3331        for (int i = 0; i < 5; i++) {
3332            streamItemPhotoValues.put(StreamItemPhotos.SORT_INDEX, i);
3333            ops.add(ContentProviderOperation.newInsert(StreamItems.CONTENT_PHOTO_URI)
3334                    .withValues(streamItemPhotoValues)
3335                    .withValueBackReference(StreamItemPhotos.STREAM_ITEM_ID, 0)
3336                    .build());
3337        }
3338        mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
3339
3340        // Check that all five photos were inserted under the raw contact.
3341        Cursor c = mResolver.query(StreamItems.CONTENT_URI, new String[]{StreamItems._ID},
3342                StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
3343                null);
3344        long streamItemId = 0;
3345        try {
3346            assertEquals(1, c.getCount());
3347            c.moveToFirst();
3348            streamItemId = c.getLong(0);
3349        } finally {
3350            c.close();
3351        }
3352
3353        c = mResolver.query(Uri.withAppendedPath(
3354                ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
3355                StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
3356                new String[]{StreamItemPhotos._ID, StreamItemPhotos.PHOTO_URI},
3357                null, null, null);
3358        try {
3359            assertEquals(5, c.getCount());
3360            byte[] expectedPhotoBytes = loadPhotoFromResource(
3361                    R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO);
3362            while (c.moveToNext()) {
3363                String photoUri = c.getString(1);
3364                assertInputStreamContent(expectedPhotoBytes,
3365                        mResolver.openInputStream(Uri.parse(photoUri)));
3366            }
3367        } finally {
3368            c.close();
3369        }
3370    }
3371
3372    // Stream item update test cases.
3373
3374    public void testUpdateStreamItemById() {
3375        long rawContactId = createRawContact();
3376        ContentValues values = buildGenericStreamItemValues();
3377        Uri resultUri = insertStreamItem(rawContactId, values, null);
3378        long streamItemId = ContentUris.parseId(resultUri);
3379        values.put(StreamItems.TEXT, "Goodbye world");
3380        mResolver.update(ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), values,
3381                null, null);
3382        assertStoredValues(Uri.withAppendedPath(
3383                ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
3384                RawContacts.StreamItems.CONTENT_DIRECTORY), values);
3385    }
3386
3387    public void testUpdateStreamItemWithContentValues() {
3388        long rawContactId = createRawContact();
3389        ContentValues values = buildGenericStreamItemValues();
3390        Uri resultUri = insertStreamItem(rawContactId, values, null);
3391        long streamItemId = ContentUris.parseId(resultUri);
3392        values.put(StreamItems._ID, streamItemId);
3393        values.put(StreamItems.TEXT, "Goodbye world");
3394        mResolver.update(StreamItems.CONTENT_URI, values, null, null);
3395        assertStoredValues(Uri.withAppendedPath(
3396                ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
3397                RawContacts.StreamItems.CONTENT_DIRECTORY), values);
3398    }
3399
3400    public void testUpdateStreamItemFromOtherAccount() {
3401        long rawContactId = createRawContact(mAccount);
3402        ContentValues values = buildGenericStreamItemValues();
3403        Uri resultUri = insertStreamItem(rawContactId, values, mAccount);
3404        long streamItemId = ContentUris.parseId(resultUri);
3405        values.put(StreamItems._ID, streamItemId);
3406        values.put(StreamItems.TEXT, "Goodbye world");
3407        try {
3408            mResolver.update(maybeAddAccountQueryParameters(StreamItems.CONTENT_URI, mAccountTwo),
3409                    values, null, null);
3410            fail("Should not be able to update stream items inserted by another account");
3411        } catch (SecurityException expected) {
3412            // Can't update the stream items from another account.
3413        }
3414    }
3415
3416    // Stream item photo update test cases.
3417
3418    public void testUpdateStreamItemPhotoById() throws IOException {
3419        long rawContactId = createRawContact();
3420        ContentValues values = buildGenericStreamItemValues();
3421        Uri resultUri = insertStreamItem(rawContactId, values, null);
3422        long streamItemId = ContentUris.parseId(resultUri);
3423        ContentValues photoValues = buildGenericStreamItemPhotoValues(1);
3424        resultUri = insertStreamItemPhoto(streamItemId, photoValues, null);
3425        long streamItemPhotoId = ContentUris.parseId(resultUri);
3426
3427        photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
3428                R.drawable.nebula, PhotoSize.ORIGINAL));
3429        Uri photoUri =
3430                ContentUris.withAppendedId(
3431                        Uri.withAppendedPath(
3432                                ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
3433                                StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
3434                        streamItemPhotoId);
3435        mResolver.update(photoUri, photoValues, null, null);
3436        photoValues.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
3437        assertStoredValues(photoUri, photoValues);
3438
3439        // Check that the photo stored is the expected one.
3440        String displayPhotoUri = getStoredValue(photoUri, StreamItemPhotos.PHOTO_URI);
3441        assertInputStreamContent(loadPhotoFromResource(R.drawable.nebula, PhotoSize.DISPLAY_PHOTO),
3442                mResolver.openInputStream(Uri.parse(displayPhotoUri)));
3443    }
3444
3445    public void testUpdateStreamItemPhotoWithContentValues() throws IOException {
3446        long rawContactId = createRawContact();
3447        ContentValues values = buildGenericStreamItemValues();
3448        Uri resultUri = insertStreamItem(rawContactId, values, null);
3449        long streamItemId = ContentUris.parseId(resultUri);
3450        ContentValues photoValues = buildGenericStreamItemPhotoValues(1);
3451        resultUri = insertStreamItemPhoto(streamItemId, photoValues, null);
3452        long streamItemPhotoId = ContentUris.parseId(resultUri);
3453
3454        photoValues.put(StreamItemPhotos._ID, streamItemPhotoId);
3455        photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
3456                R.drawable.nebula, PhotoSize.ORIGINAL));
3457        Uri photoUri =
3458                Uri.withAppendedPath(
3459                        ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
3460                        StreamItems.StreamItemPhotos.CONTENT_DIRECTORY);
3461        mResolver.update(photoUri, photoValues, null, null);
3462        photoValues.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
3463        assertStoredValues(photoUri, photoValues);
3464
3465        // Check that the photo stored is the expected one.
3466        String displayPhotoUri = getStoredValue(photoUri, StreamItemPhotos.PHOTO_URI);
3467        assertInputStreamContent(loadPhotoFromResource(R.drawable.nebula, PhotoSize.DISPLAY_PHOTO),
3468                mResolver.openInputStream(Uri.parse(displayPhotoUri)));
3469    }
3470
3471    public void testUpdateStreamItemPhotoFromOtherAccount() {
3472        long rawContactId = createRawContact(mAccount);
3473        ContentValues values = buildGenericStreamItemValues();
3474        Uri resultUri = insertStreamItem(rawContactId, values, mAccount);
3475        long streamItemId = ContentUris.parseId(resultUri);
3476        ContentValues photoValues = buildGenericStreamItemPhotoValues(1);
3477        resultUri = insertStreamItemPhoto(streamItemId, photoValues, mAccount);
3478        long streamItemPhotoId = ContentUris.parseId(resultUri);
3479
3480        photoValues.put(StreamItemPhotos._ID, streamItemPhotoId);
3481        photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
3482                R.drawable.galaxy, PhotoSize.ORIGINAL));
3483        Uri photoUri =
3484                maybeAddAccountQueryParameters(
3485                        Uri.withAppendedPath(
3486                                ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
3487                                StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
3488                        mAccountTwo);
3489        try {
3490            mResolver.update(photoUri, photoValues, null, null);
3491            fail("Should not be able to update stream item photos inserted by another account");
3492        } catch (SecurityException expected) {
3493            // Can't update a stream item photo inserted by another account.
3494        }
3495    }
3496
3497    // Stream item deletion test cases.
3498
3499    public void testDeleteStreamItemById() {
3500        long rawContactId = createRawContact();
3501        ContentValues firstValues = buildGenericStreamItemValues();
3502        Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
3503        long firstStreamItemId = ContentUris.parseId(resultUri);
3504
3505        ContentValues secondValues = buildGenericStreamItemValues();
3506        secondValues.put(StreamItems.TEXT, "Goodbye world");
3507        insertStreamItem(rawContactId, secondValues, null);
3508
3509        // Delete the first stream item.
3510        mResolver.delete(ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId),
3511                null, null);
3512
3513        // Check that only the second item remains.
3514        assertStoredValues(Uri.withAppendedPath(
3515                ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
3516                RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues);
3517    }
3518
3519    public void testDeleteStreamItemWithSelection() {
3520        long rawContactId = createRawContact();
3521        ContentValues firstValues = buildGenericStreamItemValues();
3522        insertStreamItem(rawContactId, firstValues, null);
3523
3524        ContentValues secondValues = buildGenericStreamItemValues();
3525        secondValues.put(StreamItems.TEXT, "Goodbye world");
3526        insertStreamItem(rawContactId, secondValues, null);
3527
3528        // Delete the first stream item with a custom selection.
3529        mResolver.delete(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?",
3530                new String[]{"Hello world"});
3531
3532        // Check that only the second item remains.
3533        assertStoredValues(Uri.withAppendedPath(
3534                ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
3535                RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues);
3536    }
3537
3538    public void testDeleteStreamItemFromOtherAccount() {
3539        long rawContactId = createRawContact(mAccount);
3540        long streamItemId = ContentUris.parseId(
3541                insertStreamItem(rawContactId, buildGenericStreamItemValues(), mAccount));
3542        try {
3543            mResolver.delete(
3544                    maybeAddAccountQueryParameters(
3545                            ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
3546                            mAccountTwo), null, null);
3547            fail("Should not be able to delete stream item inserted by another account");
3548        } catch (SecurityException expected) {
3549            // Can't delete a stream item from another account.
3550        }
3551    }
3552
3553    // Stream item photo deletion test cases.
3554
3555    public void testDeleteStreamItemPhotoById() {
3556        long rawContactId = createRawContact();
3557        long streamItemId = ContentUris.parseId(
3558                insertStreamItem(rawContactId, buildGenericStreamItemValues(), null));
3559        long streamItemPhotoId = ContentUris.parseId(
3560                insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), null));
3561        mResolver.delete(
3562                ContentUris.withAppendedId(
3563                        Uri.withAppendedPath(
3564                                ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
3565                                StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
3566                        streamItemPhotoId), null, null);
3567
3568        Cursor c = mResolver.query(StreamItems.CONTENT_PHOTO_URI,
3569                new String[]{StreamItemPhotos._ID},
3570                StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{String.valueOf(streamItemId)},
3571                null);
3572        try {
3573            assertEquals("Expected photo to be deleted.", 0, c.getCount());
3574        } finally {
3575            c.close();
3576        }
3577    }
3578
3579    public void testDeleteStreamItemPhotoWithSelection() {
3580        long rawContactId = createRawContact();
3581        long streamItemId = ContentUris.parseId(
3582                insertStreamItem(rawContactId, buildGenericStreamItemValues(), null));
3583        ContentValues firstPhotoValues = buildGenericStreamItemPhotoValues(0);
3584        ContentValues secondPhotoValues = buildGenericStreamItemPhotoValues(1);
3585        insertStreamItemPhoto(streamItemId, firstPhotoValues, null);
3586        firstPhotoValues.remove(StreamItemPhotos.PHOTO);  // Removed while processing.
3587        insertStreamItemPhoto(streamItemId, secondPhotoValues, null);
3588        Uri photoUri = Uri.withAppendedPath(
3589                ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
3590                StreamItems.StreamItemPhotos.CONTENT_DIRECTORY);
3591        mResolver.delete(photoUri, StreamItemPhotos.SORT_INDEX + "=1", null);
3592
3593        assertStoredValues(photoUri, firstPhotoValues);
3594    }
3595
3596    public void testDeleteStreamItemPhotoFromOtherAccount() {
3597        long rawContactId = createRawContact(mAccount);
3598        long streamItemId = ContentUris.parseId(
3599                insertStreamItem(rawContactId, buildGenericStreamItemValues(), mAccount));
3600        insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), mAccount);
3601        try {
3602            mResolver.delete(maybeAddAccountQueryParameters(
3603                    Uri.withAppendedPath(
3604                            ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
3605                            StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
3606                    mAccountTwo), null, null);
3607            fail("Should not be able to delete stream item photo inserted by another account");
3608        } catch (SecurityException expected) {
3609            // Can't delete a stream item photo from another account.
3610        }
3611    }
3612
3613    public void testQueryStreamItemLimit() {
3614        ContentValues values = new ContentValues();
3615        values.put(StreamItems.MAX_ITEMS, 5);
3616        assertStoredValues(StreamItems.CONTENT_LIMIT_URI, values);
3617    }
3618
3619    // Tests for inserting or updating stream items as a side-effect of making status updates
3620    // (forward-compatibility of status updates into the new social stream API).
3621
3622    public void testStreamItemInsertedOnStatusUpdate() {
3623
3624        // This method of creating a raw contact automatically inserts a status update with
3625        // the status message "hacking".
3626        ContentValues values = new ContentValues();
3627        long rawContactId = createRawContact(values, "18004664411",
3628                "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
3629                StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
3630                        StatusUpdates.CAPABILITY_HAS_VOICE);
3631
3632        ContentValues expectedValues = new ContentValues();
3633        expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3634        expectedValues.put(StreamItems.TEXT, "hacking");
3635        assertStoredValues(RawContacts.CONTENT_URI.buildUpon()
3636                .appendPath(String.valueOf(rawContactId))
3637                .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(),
3638                expectedValues);
3639    }
3640
3641    public void testStreamItemUpdatedOnSecondStatusUpdate() {
3642
3643        // This method of creating a raw contact automatically inserts a status update with
3644        // the status message "hacking".
3645        ContentValues values = new ContentValues();
3646        int chatMode = StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
3647                StatusUpdates.CAPABILITY_HAS_VOICE;
3648        long rawContactId = createRawContact(values, "18004664411",
3649                "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, chatMode);
3650
3651        // Insert a new status update for the raw contact.
3652        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411@acme.com",
3653                StatusUpdates.INVISIBLE, "finished hacking", chatMode);
3654
3655        ContentValues expectedValues = new ContentValues();
3656        expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
3657        expectedValues.put(StreamItems.TEXT, "finished hacking");
3658        assertStoredValues(RawContacts.CONTENT_URI.buildUpon()
3659                .appendPath(String.valueOf(rawContactId))
3660                .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(),
3661                expectedValues);
3662    }
3663
3664    private ContentValues buildGenericStreamItemValues() {
3665        ContentValues values = new ContentValues();
3666        values.put(StreamItems.TEXT, "Hello world");
3667        values.put(StreamItems.TIMESTAMP, System.currentTimeMillis());
3668        values.put(StreamItems.COMMENTS, "Reshared by 123 others");
3669        return values;
3670    }
3671
3672    private ContentValues buildGenericStreamItemPhotoValues(int sortIndex) {
3673        ContentValues values = new ContentValues();
3674        values.put(StreamItemPhotos.SORT_INDEX, sortIndex);
3675        values.put(StreamItemPhotos.PHOTO,
3676                loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.ORIGINAL));
3677        return values;
3678    }
3679
3680    public void testSingleStatusUpdateRowPerContact() {
3681        int protocol1 = Im.PROTOCOL_GOOGLE_TALK;
3682        String handle1 = "test@gmail.com";
3683
3684        long rawContactId1 = createRawContact();
3685        insertImHandle(rawContactId1, protocol1, null, handle1);
3686
3687        insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AVAILABLE, "Green",
3688                StatusUpdates.CAPABILITY_HAS_CAMERA);
3689        insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AWAY, "Yellow",
3690                StatusUpdates.CAPABILITY_HAS_CAMERA);
3691        insertStatusUpdate(protocol1, null, handle1, StatusUpdates.INVISIBLE, "Red",
3692                StatusUpdates.CAPABILITY_HAS_CAMERA);
3693
3694        Cursor c = queryContact(queryContactId(rawContactId1),
3695                new String[] {Contacts.CONTACT_PRESENCE, Contacts.CONTACT_STATUS});
3696        assertEquals(1, c.getCount());
3697
3698        c.moveToFirst();
3699        assertEquals(StatusUpdates.INVISIBLE, c.getInt(0));
3700        assertEquals("Red", c.getString(1));
3701        c.close();
3702    }
3703
3704    private void updateSendToVoicemailAndRingtone(long contactId, boolean sendToVoicemail,
3705            String ringtone) {
3706        ContentValues values = new ContentValues();
3707        values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail);
3708        if (ringtone != null) {
3709            values.put(Contacts.CUSTOM_RINGTONE, ringtone);
3710        }
3711
3712        final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
3713        int count = mResolver.update(uri, values, null, null);
3714        assertEquals(1, count);
3715    }
3716
3717    private void updateSendToVoicemailAndRingtoneWithSelection(long contactId,
3718            boolean sendToVoicemail, String ringtone) {
3719        ContentValues values = new ContentValues();
3720        values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail);
3721        if (ringtone != null) {
3722            values.put(Contacts.CUSTOM_RINGTONE, ringtone);
3723        }
3724
3725        int count = mResolver.update(Contacts.CONTENT_URI, values, Contacts._ID + "=" + contactId,
3726                null);
3727        assertEquals(1, count);
3728    }
3729
3730    private void assertSendToVoicemailAndRingtone(long contactId, boolean expectedSendToVoicemail,
3731            String expectedRingtone) {
3732        Cursor c = queryContact(contactId);
3733        assertTrue(c.moveToNext());
3734        int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL));
3735        assertEquals(expectedSendToVoicemail ? 1 : 0, sendToVoicemail);
3736        String ringtone = c.getString(c.getColumnIndex(Contacts.CUSTOM_RINGTONE));
3737        if (expectedRingtone == null) {
3738            assertNull(ringtone);
3739        } else {
3740            assertTrue(ArrayUtils.contains(expectedRingtone.split(","), ringtone));
3741        }
3742        c.close();
3743    }
3744
3745    public void testContactVisibilityUpdateOnMembershipChange() {
3746        long rawContactId = createRawContact(mAccount);
3747        assertVisibility(rawContactId, "0");
3748
3749        long visibleGroupId = createGroup(mAccount, "123", "Visible", 1);
3750        long invisibleGroupId = createGroup(mAccount, "567", "Invisible", 0);
3751
3752        Uri membership1 = insertGroupMembership(rawContactId, visibleGroupId);
3753        assertVisibility(rawContactId, "1");
3754
3755        Uri membership2 = insertGroupMembership(rawContactId, invisibleGroupId);
3756        assertVisibility(rawContactId, "1");
3757
3758        mResolver.delete(membership1, null, null);
3759        assertVisibility(rawContactId, "0");
3760
3761        ContentValues values = new ContentValues();
3762        values.put(GroupMembership.GROUP_ROW_ID, visibleGroupId);
3763
3764        mResolver.update(membership2, values, null, null);
3765        assertVisibility(rawContactId, "1");
3766    }
3767
3768    private void assertVisibility(long rawContactId, String expectedValue) {
3769        assertStoredValue(Contacts.CONTENT_URI, Contacts._ID + "=" + queryContactId(rawContactId),
3770                null, Contacts.IN_VISIBLE_GROUP, expectedValue);
3771    }
3772
3773    public void testSupplyingBothValuesAndParameters() throws Exception {
3774        Account account = new Account("account 1", "type%/:1");
3775        Uri uri = ContactsContract.Groups.CONTENT_URI.buildUpon()
3776                .appendQueryParameter(ContactsContract.Groups.ACCOUNT_NAME, account.name)
3777                .appendQueryParameter(ContactsContract.Groups.ACCOUNT_TYPE, account.type)
3778                .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
3779                .build();
3780
3781        ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(uri);
3782        builder.withValue(ContactsContract.Groups.ACCOUNT_TYPE, account.type);
3783        builder.withValue(ContactsContract.Groups.ACCOUNT_NAME, account.name);
3784        builder.withValue(ContactsContract.Groups.SYSTEM_ID, "some id");
3785        builder.withValue(ContactsContract.Groups.TITLE, "some name");
3786        builder.withValue(ContactsContract.Groups.GROUP_VISIBLE, 1);
3787
3788        mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(builder.build()));
3789
3790        builder = ContentProviderOperation.newInsert(uri);
3791        builder.withValue(ContactsContract.Groups.ACCOUNT_TYPE, account.type + "diff");
3792        builder.withValue(ContactsContract.Groups.ACCOUNT_NAME, account.name);
3793        builder.withValue(ContactsContract.Groups.SYSTEM_ID, "some other id");
3794        builder.withValue(ContactsContract.Groups.TITLE, "some other name");
3795        builder.withValue(ContactsContract.Groups.GROUP_VISIBLE, 1);
3796
3797        try {
3798            mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(builder.build()));
3799            fail("Expected IllegalArgumentException");
3800        } catch (IllegalArgumentException ex) {
3801            // Expected
3802        }
3803    }
3804
3805    public void testContentEntityIterator() {
3806        // create multiple contacts and check that the selected ones are returned
3807        long id;
3808
3809        long groupId1 = createGroup(mAccount, "gsid1", "title1");
3810        long groupId2 = createGroup(mAccount, "gsid2", "title2");
3811
3812        id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c0");
3813        insertGroupMembership(id, "gsid1");
3814        insertEmail(id, "c0@email.com");
3815        insertPhoneNumber(id, "5551212c0");
3816
3817        long c1 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c1");
3818        Uri id_1_0 = insertGroupMembership(id, "gsid1");
3819        Uri id_1_1 = insertGroupMembership(id, "gsid2");
3820        Uri id_1_2 = insertEmail(id, "c1@email.com");
3821        Uri id_1_3 = insertPhoneNumber(id, "5551212c1");
3822
3823        long c2 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c2");
3824        Uri id_2_0 = insertGroupMembership(id, "gsid1");
3825        Uri id_2_1 = insertEmail(id, "c2@email.com");
3826        Uri id_2_2 = insertPhoneNumber(id, "5551212c2");
3827
3828        long c3 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c3");
3829        Uri id_3_0 = insertGroupMembership(id, groupId2);
3830        Uri id_3_1 = insertEmail(id, "c3@email.com");
3831        Uri id_3_2 = insertPhoneNumber(id, "5551212c3");
3832
3833        EntityIterator iterator = RawContacts.newEntityIterator(mResolver.query(
3834                maybeAddAccountQueryParameters(RawContactsEntity.CONTENT_URI, mAccount), null,
3835                RawContacts.SOURCE_ID + " in ('c1', 'c2', 'c3')", null, null));
3836        Entity entity;
3837        ContentValues[] subValues;
3838        entity = iterator.next();
3839        assertEquals(c1, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
3840        subValues = asSortedContentValuesArray(entity.getSubValues());
3841        assertEquals(4, subValues.length);
3842        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
3843                Data._ID, id_1_0,
3844                GroupMembership.GROUP_ROW_ID, groupId1,
3845                GroupMembership.GROUP_SOURCE_ID, "gsid1");
3846        assertDataRow(subValues[1], GroupMembership.CONTENT_ITEM_TYPE,
3847                Data._ID, id_1_1,
3848                GroupMembership.GROUP_ROW_ID, groupId2,
3849                GroupMembership.GROUP_SOURCE_ID, "gsid2");
3850        assertDataRow(subValues[2], Email.CONTENT_ITEM_TYPE,
3851                Data._ID, id_1_2,
3852                Email.DATA, "c1@email.com");
3853        assertDataRow(subValues[3], Phone.CONTENT_ITEM_TYPE,
3854                Data._ID, id_1_3,
3855                Email.DATA, "5551212c1");
3856
3857        entity = iterator.next();
3858        assertEquals(c2, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
3859        subValues = asSortedContentValuesArray(entity.getSubValues());
3860        assertEquals(3, subValues.length);
3861        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
3862                Data._ID, id_2_0,
3863                GroupMembership.GROUP_ROW_ID, groupId1,
3864                GroupMembership.GROUP_SOURCE_ID, "gsid1");
3865        assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
3866                Data._ID, id_2_1,
3867                Email.DATA, "c2@email.com");
3868        assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
3869                Data._ID, id_2_2,
3870                Email.DATA, "5551212c2");
3871
3872        entity = iterator.next();
3873        assertEquals(c3, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
3874        subValues = asSortedContentValuesArray(entity.getSubValues());
3875        assertEquals(3, subValues.length);
3876        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
3877                Data._ID, id_3_0,
3878                GroupMembership.GROUP_ROW_ID, groupId2,
3879                GroupMembership.GROUP_SOURCE_ID, "gsid2");
3880        assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
3881                Data._ID, id_3_1,
3882                Email.DATA, "c3@email.com");
3883        assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
3884                Data._ID, id_3_2,
3885                Email.DATA, "5551212c3");
3886
3887        assertFalse(iterator.hasNext());
3888        iterator.close();
3889    }
3890
3891    public void testDataCreateUpdateDeleteByMimeType() throws Exception {
3892        long rawContactId = createRawContact();
3893
3894        ContentValues values = new ContentValues();
3895        values.put(Data.RAW_CONTACT_ID, rawContactId);
3896        values.put(Data.MIMETYPE, "testmimetype");
3897        values.put(Data.RES_PACKAGE, "oldpackage");
3898        values.put(Data.IS_PRIMARY, 1);
3899        values.put(Data.IS_SUPER_PRIMARY, 1);
3900        values.put(Data.DATA1, "old1");
3901        values.put(Data.DATA2, "old2");
3902        values.put(Data.DATA3, "old3");
3903        values.put(Data.DATA4, "old4");
3904        values.put(Data.DATA5, "old5");
3905        values.put(Data.DATA6, "old6");
3906        values.put(Data.DATA7, "old7");
3907        values.put(Data.DATA8, "old8");
3908        values.put(Data.DATA9, "old9");
3909        values.put(Data.DATA10, "old10");
3910        values.put(Data.DATA11, "old11");
3911        values.put(Data.DATA12, "old12");
3912        values.put(Data.DATA13, "old13");
3913        values.put(Data.DATA14, "old14");
3914        values.put(Data.DATA15, "old15");
3915        Uri uri = mResolver.insert(Data.CONTENT_URI, values);
3916        assertStoredValues(uri, values);
3917        assertNetworkNotified(true);
3918
3919        values.clear();
3920        values.put(Data.RES_PACKAGE, "newpackage");
3921        values.put(Data.IS_PRIMARY, 0);
3922        values.put(Data.IS_SUPER_PRIMARY, 0);
3923        values.put(Data.DATA1, "new1");
3924        values.put(Data.DATA2, "new2");
3925        values.put(Data.DATA3, "new3");
3926        values.put(Data.DATA4, "new4");
3927        values.put(Data.DATA5, "new5");
3928        values.put(Data.DATA6, "new6");
3929        values.put(Data.DATA7, "new7");
3930        values.put(Data.DATA8, "new8");
3931        values.put(Data.DATA9, "new9");
3932        values.put(Data.DATA10, "new10");
3933        values.put(Data.DATA11, "new11");
3934        values.put(Data.DATA12, "new12");
3935        values.put(Data.DATA13, "new13");
3936        values.put(Data.DATA14, "new14");
3937        values.put(Data.DATA15, "new15");
3938        mResolver.update(Data.CONTENT_URI, values, Data.RAW_CONTACT_ID + "=" + rawContactId +
3939                " AND " + Data.MIMETYPE + "='testmimetype'", null);
3940        assertNetworkNotified(true);
3941
3942        assertStoredValues(uri, values);
3943
3944        int count = mResolver.delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
3945                + " AND " + Data.MIMETYPE + "='testmimetype'", null);
3946        assertEquals(1, count);
3947        assertEquals(0, getCount(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
3948                        + " AND " + Data.MIMETYPE + "='testmimetype'", null));
3949        assertNetworkNotified(true);
3950    }
3951
3952    public void testRawContactQuery() {
3953        Account account1 = new Account("a", "b");
3954        Account account2 = new Account("c", "d");
3955        long rawContactId1 = createRawContact(account1);
3956        long rawContactId2 = createRawContact(account2);
3957
3958        Uri uri1 = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account1);
3959        Uri uri2 = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account2);
3960        assertEquals(1, getCount(uri1, null, null));
3961        assertEquals(1, getCount(uri2, null, null));
3962        assertStoredValue(uri1, RawContacts._ID, rawContactId1) ;
3963        assertStoredValue(uri2, RawContacts._ID, rawContactId2) ;
3964
3965        Uri rowUri1 = ContentUris.withAppendedId(uri1, rawContactId1);
3966        Uri rowUri2 = ContentUris.withAppendedId(uri2, rawContactId2);
3967        assertStoredValue(rowUri1, RawContacts._ID, rawContactId1) ;
3968        assertStoredValue(rowUri2, RawContacts._ID, rawContactId2) ;
3969    }
3970
3971    public void testRawContactDeletion() {
3972        long rawContactId = createRawContact(mAccount);
3973        Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
3974
3975        insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
3976        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
3977                StatusUpdates.AVAILABLE, null,
3978                StatusUpdates.CAPABILITY_HAS_CAMERA);
3979        long contactId = queryContactId(rawContactId);
3980
3981        assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
3982                null, null));
3983        assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
3984                + rawContactId, null));
3985
3986        mResolver.delete(uri, null, null);
3987
3988        assertStoredValue(uri, RawContacts.DELETED, "1");
3989        assertNetworkNotified(true);
3990
3991        Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount);
3992        mResolver.delete(permanentDeletionUri, null, null);
3993        assertEquals(0, getCount(uri, null, null));
3994        assertEquals(0, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
3995                null, null));
3996        assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
3997                + rawContactId, null));
3998        assertEquals(0, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null));
3999        assertNetworkNotified(false);
4000    }
4001
4002    public void testRawContactDeletionKeepingAggregateContact() {
4003        long rawContactId1 = createRawContactWithName(mAccount);
4004        long rawContactId2 = createRawContactWithName(mAccount);
4005        setAggregationException(
4006                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
4007
4008        long contactId = queryContactId(rawContactId1);
4009
4010        Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
4011        Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount);
4012        mResolver.delete(permanentDeletionUri, null, null);
4013        assertEquals(0, getCount(uri, null, null));
4014        assertEquals(1, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null));
4015    }
4016
4017    public void testRawContactDeletionWithAccounts() {
4018        long rawContactId = createRawContact(mAccount);
4019        Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
4020
4021        insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
4022        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
4023                StatusUpdates.AVAILABLE, null,
4024                StatusUpdates.CAPABILITY_HAS_CAMERA);
4025        assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
4026                null, null));
4027        assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
4028                + rawContactId, null));
4029
4030        // Do not delete if we are deleting with wrong account.
4031        Uri deleteWithWrongAccountUri =
4032            RawContacts.CONTENT_URI.buildUpon()
4033                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccountTwo.name)
4034                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccountTwo.type)
4035                .build();
4036        mResolver.delete(deleteWithWrongAccountUri, null, null);
4037
4038        assertStoredValue(uri, RawContacts.DELETED, "0");
4039
4040        // Delete if we are deleting with correct account.
4041        Uri deleteWithCorrectAccountUri =
4042            RawContacts.CONTENT_URI.buildUpon()
4043                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccount.name)
4044                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type)
4045                .build();
4046        mResolver.delete(deleteWithCorrectAccountUri, null, null);
4047
4048        assertStoredValue(uri, RawContacts.DELETED, "1");
4049    }
4050
4051    public void testAccountsUpdated() {
4052        // This is to ensure we do not delete contacts with null, null (account name, type)
4053        // accidentally.
4054        long rawContactId3 = createRawContactWithName("James", "Sullivan");
4055        insertPhoneNumber(rawContactId3, "5234567890");
4056        Uri rawContact3 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId3);
4057        assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null));
4058
4059        ContactsProvider2 cp = (ContactsProvider2) getProvider();
4060        mActor.setAccounts(new Account[]{mAccount, mAccountTwo});
4061        cp.onAccountsUpdated(new Account[]{mAccount, mAccountTwo});
4062        assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null));
4063        assertStoredValue(rawContact3, RawContacts.ACCOUNT_NAME, null);
4064        assertStoredValue(rawContact3, RawContacts.ACCOUNT_TYPE, null);
4065
4066        long rawContactId1 = createRawContact(mAccount);
4067        insertEmail(rawContactId1, "account1@email.com");
4068        long rawContactId2 = createRawContact(mAccountTwo);
4069        insertEmail(rawContactId2, "account2@email.com");
4070        insertImHandle(rawContactId2, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
4071        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
4072                StatusUpdates.AVAILABLE, null,
4073                StatusUpdates.CAPABILITY_HAS_CAMERA);
4074
4075        mActor.setAccounts(new Account[]{mAccount});
4076        cp.onAccountsUpdated(new Account[]{mAccount});
4077        assertEquals(2, getCount(RawContacts.CONTENT_URI, null, null));
4078        assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
4079                + rawContactId2, null));
4080    }
4081
4082    public void testAccountDeletion() {
4083        Account readOnlyAccount = new Account("act", READ_ONLY_ACCOUNT_TYPE);
4084        ContactsProvider2 cp = (ContactsProvider2) getProvider();
4085        mActor.setAccounts(new Account[]{readOnlyAccount, mAccount});
4086        cp.onAccountsUpdated(new Account[]{readOnlyAccount, mAccount});
4087
4088        long rawContactId1 = createRawContactWithName("John", "Doe", readOnlyAccount);
4089        Uri photoUri1 = insertPhoto(rawContactId1);
4090        long rawContactId2 = createRawContactWithName("john", "doe", mAccount);
4091        Uri photoUri2 = insertPhoto(rawContactId2);
4092        storeValue(photoUri2, Photo.IS_SUPER_PRIMARY, "1");
4093
4094        assertAggregated(rawContactId1, rawContactId2);
4095
4096        long contactId = queryContactId(rawContactId1);
4097
4098        // The display name should come from the writable account
4099        assertStoredValue(Uri.withAppendedPath(
4100                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
4101                Contacts.Data.CONTENT_DIRECTORY),
4102                Contacts.DISPLAY_NAME, "john doe");
4103
4104        // The photo should be the one we marked as super-primary
4105        assertStoredValue(Contacts.CONTENT_URI, contactId,
4106                Contacts.PHOTO_ID, ContentUris.parseId(photoUri2));
4107
4108        mActor.setAccounts(new Account[]{readOnlyAccount});
4109        // Remove the writable account
4110        cp.onAccountsUpdated(new Account[]{readOnlyAccount});
4111
4112        // The display name should come from the remaining account
4113        assertStoredValue(Uri.withAppendedPath(
4114                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
4115                Contacts.Data.CONTENT_DIRECTORY),
4116                Contacts.DISPLAY_NAME, "John Doe");
4117
4118        // The photo should be the remaining one
4119        assertStoredValue(Contacts.CONTENT_URI, contactId,
4120                Contacts.PHOTO_ID, ContentUris.parseId(photoUri1));
4121    }
4122
4123    public void testContactDeletion() {
4124        long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1);
4125        long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_2);
4126
4127        long contactId = queryContactId(rawContactId1);
4128
4129        mResolver.delete(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), null, null);
4130
4131        assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
4132                RawContacts.DELETED, "1");
4133        assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2),
4134                RawContacts.DELETED, "1");
4135    }
4136
4137    public void testMarkAsDirtyParameter() {
4138        long rawContactId = createRawContact(mAccount);
4139        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
4140
4141        Uri uri = insertStructuredName(rawContactId, "John", "Doe");
4142        clearDirty(rawContactUri);
4143        Uri updateUri = setCallerIsSyncAdapter(uri, mAccount);
4144
4145        ContentValues values = new ContentValues();
4146        values.put(StructuredName.FAMILY_NAME, "Dough");
4147        mResolver.update(updateUri, values, null, null);
4148        assertStoredValue(uri, StructuredName.FAMILY_NAME, "Dough");
4149        assertDirty(rawContactUri, false);
4150        assertNetworkNotified(false);
4151    }
4152
4153    public void testRawContactDirtyAndVersion() {
4154        final long rawContactId = createRawContact(mAccount);
4155        Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId);
4156        assertDirty(uri, false);
4157        long version = getVersion(uri);
4158
4159        ContentValues values = new ContentValues();
4160        values.put(ContactsContract.RawContacts.DIRTY, 0);
4161        values.put(ContactsContract.RawContacts.SEND_TO_VOICEMAIL, 1);
4162        values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
4163                RawContacts.AGGREGATION_MODE_IMMEDIATE);
4164        values.put(ContactsContract.RawContacts.STARRED, 1);
4165        assertEquals(1, mResolver.update(uri, values, null, null));
4166        assertEquals(version, getVersion(uri));
4167
4168        assertDirty(uri, false);
4169        assertNetworkNotified(false);
4170
4171        Uri emailUri = insertEmail(rawContactId, "goo@woo.com");
4172        assertDirty(uri, true);
4173        assertNetworkNotified(true);
4174        ++version;
4175        assertEquals(version, getVersion(uri));
4176        clearDirty(uri);
4177
4178        values = new ContentValues();
4179        values.put(Email.DATA, "goo@hoo.com");
4180        mResolver.update(emailUri, values, null, null);
4181        assertDirty(uri, true);
4182        assertNetworkNotified(true);
4183        ++version;
4184        assertEquals(version, getVersion(uri));
4185        clearDirty(uri);
4186
4187        mResolver.delete(emailUri, null, null);
4188        assertDirty(uri, true);
4189        assertNetworkNotified(true);
4190        ++version;
4191        assertEquals(version, getVersion(uri));
4192    }
4193
4194    public void testRawContactClearDirty() {
4195        final long rawContactId = createRawContact(mAccount);
4196        Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
4197                rawContactId);
4198        long version = getVersion(uri);
4199        insertEmail(rawContactId, "goo@woo.com");
4200        assertDirty(uri, true);
4201        version++;
4202        assertEquals(version, getVersion(uri));
4203
4204        clearDirty(uri);
4205        assertDirty(uri, false);
4206        assertEquals(version, getVersion(uri));
4207    }
4208
4209    public void testRawContactDeletionSetsDirty() {
4210        final long rawContactId = createRawContact(mAccount);
4211        Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
4212                rawContactId);
4213        long version = getVersion(uri);
4214        clearDirty(uri);
4215        assertDirty(uri, false);
4216
4217        mResolver.delete(uri, null, null);
4218        assertStoredValue(uri, RawContacts.DELETED, "1");
4219        assertDirty(uri, true);
4220        assertNetworkNotified(true);
4221        version++;
4222        assertEquals(version, getVersion(uri));
4223    }
4224
4225    public void testDeleteContactWithoutName() {
4226        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
4227        long rawContactId = ContentUris.parseId(rawContactUri);
4228
4229        Uri phoneUri = insertPhoneNumber(rawContactId, "555-123-45678", true);
4230
4231        long contactId = queryContactId(rawContactId);
4232        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4233        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
4234
4235        int numDeleted = mResolver.delete(lookupUri, null, null);
4236        assertEquals(1, numDeleted);
4237    }
4238
4239    public void testDeleteContactWithoutAnyData() {
4240        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
4241        long rawContactId = ContentUris.parseId(rawContactUri);
4242
4243        long contactId = queryContactId(rawContactId);
4244        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4245        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
4246
4247        int numDeleted = mResolver.delete(lookupUri, null, null);
4248        assertEquals(1, numDeleted);
4249    }
4250
4251    public void testDeleteContactWithEscapedUri() {
4252        ContentValues values = new ContentValues();
4253        values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
4254        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
4255        long rawContactId = ContentUris.parseId(rawContactUri);
4256
4257        long contactId = queryContactId(rawContactId);
4258        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4259        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
4260        assertEquals(1, mResolver.delete(lookupUri, null, null));
4261    }
4262
4263    public void testQueryContactWithEscapedUri() {
4264        ContentValues values = new ContentValues();
4265        values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
4266        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
4267        long rawContactId = ContentUris.parseId(rawContactUri);
4268
4269        long contactId = queryContactId(rawContactId);
4270        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4271        Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
4272        Cursor c = mResolver.query(lookupUri, null, null, null, "");
4273        assertEquals(1, c.getCount());
4274        c.close();
4275    }
4276
4277    public void testGetPhotoUri() {
4278        ContentValues values = new ContentValues();
4279        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
4280        long rawContactId = ContentUris.parseId(rawContactUri);
4281        insertStructuredName(rawContactId, "John", "Doe");
4282        long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
4283        long photoFileId = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?",
4284                new String[]{String.valueOf(dataId)}, Photo.PHOTO_FILE_ID);
4285        String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId)
4286                .toString();
4287
4288        assertStoredValue(
4289                ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId)),
4290                Contacts.PHOTO_URI, photoUri);
4291    }
4292
4293    public void testInputStreamForPhoto() throws Exception {
4294        long rawContactId = createRawContact();
4295        long contactId = queryContactId(rawContactId);
4296        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4297        insertPhoto(rawContactId);
4298        Uri photoUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_URI));
4299        Uri photoThumbnailUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI));
4300
4301        assertInputStreamContent(loadTestPhoto(PhotoSize.DISPLAY_PHOTO),
4302                mResolver.openInputStream(photoUri));
4303        assertInputStreamContent(loadTestPhoto(PhotoSize.THUMBNAIL),
4304                mResolver.openInputStream(photoThumbnailUri));
4305    }
4306
4307    private static void assertInputStreamContent(byte[] expected, InputStream is)
4308            throws IOException {
4309        try {
4310            byte[] observed = new byte[expected.length];
4311            int count = is.read(observed);
4312            assertEquals(expected.length, count);
4313            assertEquals(-1, is.read());
4314            MoreAsserts.assertEquals(expected, observed);
4315        } finally {
4316            is.close();
4317        }
4318    }
4319
4320    public void testSuperPrimaryPhoto() {
4321        long rawContactId1 = createRawContact(new Account("a", "a"));
4322        Uri photoUri1 = insertPhoto(rawContactId1, R.drawable.earth_normal);
4323        long photoId1 = ContentUris.parseId(photoUri1);
4324
4325        long rawContactId2 = createRawContact(new Account("b", "b"));
4326        Uri photoUri2 = insertPhoto(rawContactId2, R.drawable.earth_normal);
4327        long photoId2 = ContentUris.parseId(photoUri2);
4328
4329        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
4330                rawContactId1, rawContactId2);
4331
4332        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
4333                queryContactId(rawContactId1));
4334
4335        long photoFileId1 = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?",
4336                new String[]{String.valueOf(photoId1)}, Photo.PHOTO_FILE_ID);
4337        String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId1)
4338                .toString();
4339        assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
4340        assertStoredValue(contactUri, Contacts.PHOTO_URI, photoUri);
4341
4342        setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
4343                rawContactId1, rawContactId2);
4344
4345        ContentValues values = new ContentValues();
4346        values.put(Data.IS_SUPER_PRIMARY, 1);
4347        mResolver.update(photoUri2, values, null, null);
4348
4349        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
4350                rawContactId1, rawContactId2);
4351        contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
4352                queryContactId(rawContactId1));
4353        assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId2);
4354
4355        mResolver.update(photoUri1, values, null, null);
4356        assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
4357    }
4358
4359    public void testUpdatePhoto() {
4360        ContentValues values = new ContentValues();
4361        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
4362        long rawContactId = ContentUris.parseId(rawContactUri);
4363        insertStructuredName(rawContactId, "John", "Doe");
4364
4365        Uri twigUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
4366                queryContactId(rawContactId)), Contacts.Photo.CONTENT_DIRECTORY);
4367
4368        values.clear();
4369        values.put(Data.RAW_CONTACT_ID, rawContactId);
4370        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
4371        values.putNull(Photo.PHOTO);
4372        Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
4373        long photoId = ContentUris.parseId(dataUri);
4374
4375        assertEquals(0, getCount(twigUri, null, null));
4376
4377        values.clear();
4378        values.put(Photo.PHOTO, loadTestPhoto());
4379        mResolver.update(dataUri, values, null, null);
4380        assertNetworkNotified(true);
4381
4382        long twigId = getStoredLongValue(twigUri, Data._ID);
4383        assertEquals(photoId, twigId);
4384    }
4385
4386    public void testUpdateRawContactDataPhoto() {
4387        // setup a contact with a null photo
4388        ContentValues values = new ContentValues();
4389        Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
4390        long rawContactId = ContentUris.parseId(rawContactUri);
4391
4392        // setup a photo
4393        values.put(Data.RAW_CONTACT_ID, rawContactId);
4394        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
4395        values.putNull(Photo.PHOTO);
4396
4397        // try to do an update before insert should return count == 0
4398        Uri dataUri = Uri.withAppendedPath(
4399                ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
4400                RawContacts.Data.CONTENT_DIRECTORY);
4401        assertEquals(0, mResolver.update(dataUri, values, Data.MIMETYPE + "=?",
4402                new String[] {Photo.CONTENT_ITEM_TYPE}));
4403
4404        mResolver.insert(Data.CONTENT_URI, values);
4405
4406        // save a photo to the db
4407        values.clear();
4408        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
4409        values.put(Photo.PHOTO, loadTestPhoto());
4410        assertEquals(1, mResolver.update(dataUri, values, Data.MIMETYPE + "=?",
4411                new String[] {Photo.CONTENT_ITEM_TYPE}));
4412
4413        // verify the photo
4414        Cursor storedPhoto = mResolver.query(dataUri, new String[] {Photo.PHOTO},
4415                Data.MIMETYPE + "=?", new String[] {Photo.CONTENT_ITEM_TYPE}, null);
4416        storedPhoto.moveToFirst();
4417        MoreAsserts.assertEquals(loadTestPhoto(PhotoSize.THUMBNAIL), storedPhoto.getBlob(0));
4418        storedPhoto.close();
4419    }
4420
4421    public void testOpenDisplayPhotoForContactId() throws IOException {
4422        long rawContactId = createRawContactWithName();
4423        long contactId = queryContactId(rawContactId);
4424        insertPhoto(rawContactId, R.drawable.earth_normal);
4425        Uri photoUri = Contacts.CONTENT_URI.buildUpon()
4426                .appendPath(String.valueOf(contactId))
4427                .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
4428        assertInputStreamContent(
4429                loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
4430                mResolver.openInputStream(photoUri));
4431    }
4432
4433    public void testOpenDisplayPhotoForContactLookupKey() throws IOException {
4434        long rawContactId = createRawContactWithName();
4435        long contactId = queryContactId(rawContactId);
4436        String lookupKey = queryLookupKey(contactId);
4437        insertPhoto(rawContactId, R.drawable.earth_normal);
4438        Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon()
4439                .appendPath(lookupKey)
4440                .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
4441        assertInputStreamContent(
4442                loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
4443                mResolver.openInputStream(photoUri));
4444    }
4445
4446    public void testOpenDisplayPhotoForContactLookupKeyAndId() throws IOException {
4447        long rawContactId = createRawContactWithName();
4448        long contactId = queryContactId(rawContactId);
4449        String lookupKey = queryLookupKey(contactId);
4450        insertPhoto(rawContactId, R.drawable.earth_normal);
4451        Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon()
4452                .appendPath(lookupKey)
4453                .appendPath(String.valueOf(contactId))
4454                .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
4455        assertInputStreamContent(
4456                loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
4457                mResolver.openInputStream(photoUri));
4458    }
4459
4460    public void testOpenDisplayPhotoForRawContactId() throws IOException {
4461        long rawContactId = createRawContactWithName();
4462        insertPhoto(rawContactId, R.drawable.earth_normal);
4463        Uri photoUri = RawContacts.CONTENT_URI.buildUpon()
4464                .appendPath(String.valueOf(rawContactId))
4465                .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
4466        assertInputStreamContent(
4467                loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
4468                mResolver.openInputStream(photoUri));
4469    }
4470
4471    public void testOpenDisplayPhotoByPhotoUri() throws IOException {
4472        long rawContactId = createRawContactWithName();
4473        long contactId = queryContactId(rawContactId);
4474        insertPhoto(rawContactId, R.drawable.earth_normal);
4475
4476        // Get the photo URI out and check the content.
4477        String photoUri = getStoredValue(
4478                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
4479                Contacts.PHOTO_URI);
4480        assertInputStreamContent(
4481                loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
4482                mResolver.openInputStream(Uri.parse(photoUri)));
4483    }
4484
4485    public void testPhotoUriForDisplayPhoto() {
4486        long rawContactId = createRawContactWithName();
4487        long contactId = queryContactId(rawContactId);
4488
4489        // Photo being inserted is larger than a thumbnail, so it will be stored as a file.
4490        long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
4491        String photoFileId = getStoredValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
4492                Photo.PHOTO_FILE_ID);
4493        String photoUri = getStoredValue(
4494                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
4495                Contacts.PHOTO_URI);
4496
4497        // Check that the photo URI differs from the thumbnail.
4498        String thumbnailUri = getStoredValue(
4499                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
4500                Contacts.PHOTO_THUMBNAIL_URI);
4501        assertFalse(photoUri.equals(thumbnailUri));
4502
4503        // URI should be of the form display_photo/ID
4504        assertEquals(Uri.withAppendedPath(DisplayPhoto.CONTENT_URI, photoFileId).toString(),
4505                photoUri);
4506    }
4507
4508    public void testPhotoUriForThumbnailPhoto() throws IOException {
4509        long rawContactId = createRawContactWithName();
4510        long contactId = queryContactId(rawContactId);
4511
4512        // Photo being inserted is a thumbnail, so it will only be stored in a BLOB.  The photo URI
4513        // will fall back to the thumbnail URI.
4514        insertPhoto(rawContactId, R.drawable.earth_small);
4515        String photoUri = getStoredValue(
4516                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
4517                Contacts.PHOTO_URI);
4518
4519        // Check that the photo URI is equal to the thumbnail URI.
4520        String thumbnailUri = getStoredValue(
4521                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
4522                Contacts.PHOTO_THUMBNAIL_URI);
4523        assertEquals(photoUri, thumbnailUri);
4524
4525        // URI should be of the form contacts/ID/photo
4526        assertEquals(Uri.withAppendedPath(
4527                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
4528                Contacts.Photo.CONTENT_DIRECTORY).toString(),
4529                photoUri);
4530
4531        // Loading the photo URI content should get the thumbnail.
4532        assertInputStreamContent(
4533                loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL),
4534                mResolver.openInputStream(Uri.parse(photoUri)));
4535    }
4536
4537    public void testWriteNewPhotoToAssetFile() throws IOException {
4538        long rawContactId = createRawContactWithName();
4539        long contactId = queryContactId(rawContactId);
4540
4541        // Load in a huge photo.
4542        byte[] originalPhoto = loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL);
4543
4544        // Write it out.
4545        Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon()
4546                .appendPath(String.valueOf(rawContactId))
4547                .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
4548        OutputStream os = mResolver.openOutputStream(writeablePhotoUri, "rw");
4549        try {
4550            os.write(originalPhoto);
4551        } finally {
4552            os.close();
4553        }
4554
4555        // Check that the display photo and thumbnail have been set.
4556        String photoUri = getStoredValue(
4557                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI);
4558        assertFalse(TextUtils.isEmpty(photoUri));
4559        String thumbnailUri = getStoredValue(
4560                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
4561                Contacts.PHOTO_THUMBNAIL_URI);
4562        assertFalse(TextUtils.isEmpty(thumbnailUri));
4563        assertFalse(photoUri.equals(thumbnailUri));
4564
4565        // Check the content of the display photo and thumbnail.
4566        assertInputStreamContent(
4567                loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO),
4568                mResolver.openInputStream(Uri.parse(photoUri)));
4569        assertInputStreamContent(
4570                loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL),
4571                mResolver.openInputStream(Uri.parse(thumbnailUri)));
4572    }
4573
4574    public void testWriteUpdatedPhotoToAssetFile() throws IOException {
4575        long rawContactId = createRawContactWithName();
4576        long contactId = queryContactId(rawContactId);
4577
4578        // Insert a large photo first.
4579        insertPhoto(rawContactId, R.drawable.earth_large);
4580        String largeEarthPhotoUri = getStoredValue(
4581                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI);
4582
4583        // Load in a huge photo.
4584        byte[] originalPhoto = loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL);
4585
4586        // Write it out.
4587        Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon()
4588                .appendPath(String.valueOf(rawContactId))
4589                .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
4590        OutputStream os = mResolver.openOutputStream(writeablePhotoUri, "rw");
4591        try {
4592            os.write(originalPhoto);
4593        } finally {
4594            os.close();
4595        }
4596
4597        // Check that the display photo URI has been modified.
4598        String hugeEarthPhotoUri = getStoredValue(
4599                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI);
4600        assertFalse(hugeEarthPhotoUri.equals(largeEarthPhotoUri));
4601
4602        // Check the content of the display photo and thumbnail.
4603        String hugeEarthThumbnailUri = getStoredValue(
4604                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
4605                Contacts.PHOTO_THUMBNAIL_URI);
4606        assertInputStreamContent(
4607                loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO),
4608                mResolver.openInputStream(Uri.parse(hugeEarthPhotoUri)));
4609        assertInputStreamContent(
4610                loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL),
4611                mResolver.openInputStream(Uri.parse(hugeEarthThumbnailUri)));
4612
4613    }
4614
4615    public void testPhotoDimensionLimits() {
4616        ContentValues values = new ContentValues();
4617        values.put(DisplayPhoto.DISPLAY_MAX_DIM, 256);
4618        values.put(DisplayPhoto.THUMBNAIL_MAX_DIM, 96);
4619        assertStoredValues(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, values);
4620    }
4621
4622    public void testPhotoStoreCleanup() throws IOException {
4623        SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider;
4624
4625        // Trigger an initial cleanup so another one won't happen while we're running this test.
4626        provider.cleanupPhotoStore();
4627
4628        // Insert a couple of contacts with photos.
4629        long rawContactId1 = createRawContactWithName();
4630        long contactId1 = queryContactId(rawContactId1);
4631        long dataId1 = ContentUris.parseId(insertPhoto(rawContactId1, R.drawable.earth_normal));
4632        long photoFileId1 =
4633                getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId1),
4634                        Photo.PHOTO_FILE_ID);
4635
4636        long rawContactId2 = createRawContactWithName();
4637        long contactId2 = queryContactId(rawContactId2);
4638        long dataId2 = ContentUris.parseId(insertPhoto(rawContactId2, R.drawable.earth_normal));
4639        long photoFileId2 =
4640                getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2),
4641                        Photo.PHOTO_FILE_ID);
4642
4643        // Update the second raw contact with a different photo.
4644        ContentValues values = new ContentValues();
4645        values.put(Data.RAW_CONTACT_ID, rawContactId2);
4646        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
4647        values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL));
4648        assertEquals(1, mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?",
4649                new String[]{String.valueOf(dataId2)}));
4650        long replacementPhotoFileId =
4651                getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2),
4652                        Photo.PHOTO_FILE_ID);
4653
4654        // Insert a third raw contact that has a bogus photo file ID.
4655        long bogusFileId = 1234567;
4656        long rawContactId3 = createRawContactWithName();
4657        long contactId3 = queryContactId(rawContactId3);
4658        values.clear();
4659        values.put(Data.RAW_CONTACT_ID, rawContactId3);
4660        values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
4661        values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_normal,
4662                PhotoSize.THUMBNAIL));
4663        values.put(Photo.PHOTO_FILE_ID, bogusFileId);
4664        values.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
4665        mResolver.insert(Data.CONTENT_URI, values);
4666
4667        // Also insert a bogus photo that nobody is using.
4668        PhotoStore photoStore = provider.getPhotoStore();
4669        long bogusPhotoId = photoStore.insert(new PhotoProcessor(loadPhotoFromResource(
4670                R.drawable.earth_huge, PhotoSize.ORIGINAL), 256, 96));
4671
4672        // Manually trigger another cleanup in the provider.
4673        provider.cleanupPhotoStore();
4674
4675        // The following things should have happened.
4676
4677        // 1. Raw contact 1 and its photo remain unaffected.
4678        assertEquals(photoFileId1, (long) getStoredLongValue(
4679                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1),
4680                Contacts.PHOTO_FILE_ID));
4681
4682        // 2. Raw contact 2 retains its new photo.  The old one is deleted from the photo store.
4683        assertEquals(replacementPhotoFileId, (long) getStoredLongValue(
4684                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2),
4685                Contacts.PHOTO_FILE_ID));
4686        assertNull(photoStore.get(photoFileId2));
4687
4688        // 3. Raw contact 3 should have its photo file reference cleared.
4689        assertNull(getStoredValue(
4690                ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId3),
4691                Contacts.PHOTO_FILE_ID));
4692
4693        // 4. The bogus photo that nobody was using should be cleared from the photo store.
4694        assertNull(photoStore.get(bogusPhotoId));
4695    }
4696
4697    public void testOverwritePhotoWithThumbnail() throws IOException {
4698        long rawContactId = createRawContactWithName();
4699        long contactId = queryContactId(rawContactId);
4700        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4701
4702        // Write a regular-size photo.
4703        long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
4704        Long photoFileId = getStoredLongValue(contactUri, Contacts.PHOTO_FILE_ID);
4705        assertTrue(photoFileId != null && photoFileId > 0);
4706
4707        // Now overwrite the photo with a thumbnail-sized photo.
4708        ContentValues update = new ContentValues();
4709        update.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_small, PhotoSize.ORIGINAL));
4710        mResolver.update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), update, null, null);
4711
4712        // Photo file ID should have been nulled out, and the photo URI should be the same as the
4713        // thumbnail URI.
4714        assertNull(getStoredValue(contactUri, Contacts.PHOTO_FILE_ID));
4715        String photoUri = getStoredValue(contactUri, Contacts.PHOTO_URI);
4716        String thumbnailUri = getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI);
4717        assertEquals(photoUri, thumbnailUri);
4718
4719        // Retrieving the photo URI should get the thumbnail content.
4720        assertInputStreamContent(loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL),
4721                mResolver.openInputStream(Uri.parse(photoUri)));
4722    }
4723
4724    public void testUpdateRawContactSetStarred() {
4725        long rawContactId1 = createRawContactWithName();
4726        Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
4727        long rawContactId2 = createRawContactWithName();
4728        Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
4729        setAggregationException(
4730                AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
4731
4732        long contactId = queryContactId(rawContactId1);
4733        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4734        assertStoredValue(contactUri, Contacts.STARRED, "0");
4735
4736        ContentValues values = new ContentValues();
4737        values.put(RawContacts.STARRED, "1");
4738
4739        mResolver.update(rawContactUri1, values, null, null);
4740
4741        assertStoredValue(rawContactUri1, RawContacts.STARRED, "1");
4742        assertStoredValue(rawContactUri2, RawContacts.STARRED, "0");
4743        assertStoredValue(contactUri, Contacts.STARRED, "1");
4744
4745        values.put(RawContacts.STARRED, "0");
4746        mResolver.update(rawContactUri1, values, null, null);
4747
4748        assertStoredValue(rawContactUri1, RawContacts.STARRED, "0");
4749        assertStoredValue(rawContactUri2, RawContacts.STARRED, "0");
4750        assertStoredValue(contactUri, Contacts.STARRED, "0");
4751
4752        values.put(Contacts.STARRED, "1");
4753        mResolver.update(contactUri, values, null, null);
4754
4755        assertStoredValue(rawContactUri1, RawContacts.STARRED, "1");
4756        assertStoredValue(rawContactUri2, RawContacts.STARRED, "1");
4757        assertStoredValue(contactUri, Contacts.STARRED, "1");
4758    }
4759
4760    public void testSetAndClearSuperPrimaryEmail() {
4761        long rawContactId1 = createRawContact(new Account("a", "a"));
4762        Uri mailUri11 = insertEmail(rawContactId1, "test1@domain1.com");
4763        Uri mailUri12 = insertEmail(rawContactId1, "test2@domain1.com");
4764
4765        long rawContactId2 = createRawContact(new Account("b", "b"));
4766        Uri mailUri21 = insertEmail(rawContactId2, "test1@domain2.com");
4767        Uri mailUri22 = insertEmail(rawContactId2, "test2@domain2.com");
4768
4769        assertStoredValue(mailUri11, Data.IS_PRIMARY, 0);
4770        assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0);
4771        assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
4772        assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
4773        assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
4774        assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
4775        assertStoredValue(mailUri22, Data.IS_PRIMARY, 0);
4776        assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 0);
4777
4778        // Set super primary on the first pair, primary on the second
4779        {
4780            ContentValues values = new ContentValues();
4781            values.put(Data.IS_SUPER_PRIMARY, 1);
4782            mResolver.update(mailUri11, values, null, null);
4783        }
4784        {
4785            ContentValues values = new ContentValues();
4786            values.put(Data.IS_SUPER_PRIMARY, 1);
4787            mResolver.update(mailUri22, values, null, null);
4788        }
4789
4790        assertStoredValue(mailUri11, Data.IS_PRIMARY, 1);
4791        assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 1);
4792        assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
4793        assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
4794        assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
4795        assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
4796        assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
4797        assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
4798
4799        // Clear primary on the first pair, make sure second is not affected and super_primary is
4800        // also cleared
4801        {
4802            ContentValues values = new ContentValues();
4803            values.put(Data.IS_PRIMARY, 0);
4804            mResolver.update(mailUri11, values, null, null);
4805        }
4806
4807        assertStoredValue(mailUri11, Data.IS_PRIMARY, 0);
4808        assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0);
4809        assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
4810        assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
4811        assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
4812        assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
4813        assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
4814        assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
4815
4816        // Ensure that we can only clear super_primary, if we specify the correct data row
4817        {
4818            ContentValues values = new ContentValues();
4819            values.put(Data.IS_SUPER_PRIMARY, 0);
4820            mResolver.update(mailUri21, values, null, null);
4821        }
4822
4823        assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
4824        assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
4825        assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
4826        assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
4827
4828        // Ensure that we can only clear primary, if we specify the correct data row
4829        {
4830            ContentValues values = new ContentValues();
4831            values.put(Data.IS_PRIMARY, 0);
4832            mResolver.update(mailUri21, values, null, null);
4833        }
4834
4835        assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
4836        assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
4837        assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
4838        assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
4839
4840        // Now clear super-primary for real
4841        {
4842            ContentValues values = new ContentValues();
4843            values.put(Data.IS_SUPER_PRIMARY, 0);
4844            mResolver.update(mailUri22, values, null, null);
4845        }
4846
4847        assertStoredValue(mailUri11, Data.IS_PRIMARY, 0);
4848        assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0);
4849        assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
4850        assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
4851        assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
4852        assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
4853        assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
4854        assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 0);
4855    }
4856
4857    /**
4858     * Common function for the testNewPrimaryIn* functions. Its four configurations
4859     * are each called from its own test
4860     */
4861    public void testChangingPrimary(boolean inUpdate, boolean withSuperPrimary) {
4862        long rawContactId = createRawContact(new Account("a", "a"));
4863        Uri mailUri1 = insertEmail(rawContactId, "test1@domain1.com", true);
4864
4865        if (withSuperPrimary) {
4866            final ContentValues values = new ContentValues();
4867            values.put(Data.IS_SUPER_PRIMARY, 1);
4868            mResolver.update(mailUri1, values, null, null);
4869        }
4870
4871        assertStoredValue(mailUri1, Data.IS_PRIMARY, 1);
4872        assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0);
4873
4874        // Insert another item
4875        final Uri mailUri2;
4876        if (inUpdate) {
4877            mailUri2 = insertEmail(rawContactId, "test2@domain1.com");
4878
4879            assertStoredValue(mailUri1, Data.IS_PRIMARY, 1);
4880            assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0);
4881            assertStoredValue(mailUri2, Data.IS_PRIMARY, 0);
4882            assertStoredValue(mailUri2, Data.IS_SUPER_PRIMARY, 0);
4883
4884            final ContentValues values = new ContentValues();
4885            values.put(Data.IS_PRIMARY, 1);
4886            mResolver.update(mailUri2, values, null, null);
4887        } else {
4888            // directly add as default
4889            mailUri2 = insertEmail(rawContactId, "test2@domain1.com", true);
4890        }
4891
4892        // Ensure that primary has been unset on the first
4893        // If withSuperPrimary is set, also ensure that is has been moved to the new item
4894        assertStoredValue(mailUri1, Data.IS_PRIMARY, 0);
4895        assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, 0);
4896        assertStoredValue(mailUri2, Data.IS_PRIMARY, 1);
4897        assertStoredValue(mailUri2, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0);
4898    }
4899
4900    public void testNewPrimaryInInsert() {
4901        testChangingPrimary(false, false);
4902    }
4903
4904    public void testNewPrimaryInInsertWithSuperPrimary() {
4905        testChangingPrimary(false, true);
4906    }
4907
4908    public void testNewPrimaryInUpdate() {
4909        testChangingPrimary(true, false);
4910    }
4911
4912    public void testNewPrimaryInUpdateWithSuperPrimary() {
4913        testChangingPrimary(true, true);
4914    }
4915
4916    public void testLiveFolders() {
4917        long rawContactId1 = createRawContactWithName("James", "Sullivan");
4918        insertPhoneNumber(rawContactId1, "5234567890");
4919        long contactId1 = queryContactId(rawContactId1);
4920
4921        long rawContactId2 = createRawContactWithName("Mike", "Wazowski");
4922        long contactId2 = queryContactId(rawContactId2);
4923        storeValue(Contacts.CONTENT_URI, contactId2, Contacts.STARRED, "1");
4924
4925        long rawContactId3 = createRawContactWithName("Randall", "Boggs");
4926        long contactId3 = queryContactId(rawContactId3);
4927        long groupId = createGroup(NO_ACCOUNT, "src1", "VIP");
4928        insertGroupMembership(rawContactId3, groupId);
4929
4930        assertLiveFolderContents(
4931                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
4932                        "live_folders/contacts"),
4933                contactId1, "James Sullivan",
4934                contactId2, "Mike Wazowski",
4935                contactId3, "Randall Boggs");
4936
4937        assertLiveFolderContents(
4938                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
4939                        "live_folders/contacts_with_phones"),
4940                contactId1, "James Sullivan");
4941
4942        assertLiveFolderContents(
4943                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
4944                        "live_folders/favorites"),
4945                contactId2, "Mike Wazowski");
4946
4947        assertLiveFolderContents(
4948                Uri.withAppendedPath(Uri.withAppendedPath(ContactsContract.AUTHORITY_URI,
4949                        "live_folders/contacts"), Uri.encode("VIP")),
4950                contactId3, "Randall Boggs");
4951    }
4952
4953    private void assertLiveFolderContents(Uri uri, Object... expected) {
4954        Cursor c = mResolver.query(uri, new String[]{LiveFolders._ID, LiveFolders.NAME},
4955                null, null, LiveFolders._ID);
4956        assertEquals(expected.length/2, c.getCount());
4957        for (int i = 0; i < expected.length/2; i++) {
4958            assertTrue(c.moveToNext());
4959            assertEquals(((Long)expected[i * 2]).longValue(), c.getLong(0));
4960            assertEquals(expected[i * 2 + 1], c.getString(1));
4961        }
4962        c.close();
4963    }
4964
4965    public void testContactCounts() {
4966        Uri uri = Contacts.CONTENT_URI.buildUpon()
4967                .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
4968
4969        createRawContact();
4970        createRawContactWithName("James", "Sullivan");
4971        createRawContactWithName("The Abominable", "Snowman");
4972        createRawContactWithName("Mike", "Wazowski");
4973        createRawContactWithName("randall", "boggs");
4974        createRawContactWithName("Boo", null);
4975        createRawContactWithName("Mary", null);
4976        createRawContactWithName("Roz", null);
4977
4978        Cursor cursor = mResolver.query(uri,
4979                new String[]{Contacts.DISPLAY_NAME},
4980                null, null, Contacts.SORT_KEY_PRIMARY + " COLLATE LOCALIZED");
4981
4982        assertFirstLetterValues(cursor, null, "B", "J", "M", "R", "T");
4983        assertFirstLetterCounts(cursor,    1,   1,   1,   2,   2,   1);
4984        cursor.close();
4985
4986        cursor = mResolver.query(uri,
4987                new String[]{Contacts.DISPLAY_NAME},
4988                null, null, Contacts.SORT_KEY_ALTERNATIVE + " COLLATE LOCALIZED DESC");
4989
4990        assertFirstLetterValues(cursor, "W", "S", "R", "M", "B", null);
4991        assertFirstLetterCounts(cursor,   1,   2,   1,   1,   2,    1);
4992        cursor.close();
4993    }
4994
4995    private void assertFirstLetterValues(Cursor cursor, String... expected) {
4996        String[] actual = cursor.getExtras()
4997                .getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
4998        MoreAsserts.assertEquals(expected, actual);
4999    }
5000
5001    private void assertFirstLetterCounts(Cursor cursor, int... expected) {
5002        int[] actual = cursor.getExtras()
5003                .getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
5004        MoreAsserts.assertEquals(expected, actual);
5005    }
5006
5007    public void testReadBooleanQueryParameter() {
5008        assertBooleanUriParameter("foo:bar", "bool", true, true);
5009        assertBooleanUriParameter("foo:bar", "bool", false, false);
5010        assertBooleanUriParameter("foo:bar?bool=0", "bool", true, false);
5011        assertBooleanUriParameter("foo:bar?bool=1", "bool", false, true);
5012        assertBooleanUriParameter("foo:bar?bool=false", "bool", true, false);
5013        assertBooleanUriParameter("foo:bar?bool=true", "bool", false, true);
5014        assertBooleanUriParameter("foo:bar?bool=FaLsE", "bool", true, false);
5015        assertBooleanUriParameter("foo:bar?bool=false&some=some", "bool", true, false);
5016        assertBooleanUriParameter("foo:bar?bool=1&some=some", "bool", false, true);
5017        assertBooleanUriParameter("foo:bar?some=bool", "bool", true, true);
5018        assertBooleanUriParameter("foo:bar?bool", "bool", true, true);
5019    }
5020
5021    private void assertBooleanUriParameter(String uriString, String parameter,
5022            boolean defaultValue, boolean expectedValue) {
5023        assertEquals(expectedValue, ContactsProvider2.readBooleanQueryParameter(
5024                Uri.parse(uriString), parameter, defaultValue));
5025    }
5026
5027    public void testGetQueryParameter() {
5028        assertQueryParameter("foo:bar", "param", null);
5029        assertQueryParameter("foo:bar?param", "param", null);
5030        assertQueryParameter("foo:bar?param=", "param", "");
5031        assertQueryParameter("foo:bar?param=val", "param", "val");
5032        assertQueryParameter("foo:bar?param=val&some=some", "param", "val");
5033        assertQueryParameter("foo:bar?some=some&param=val", "param", "val");
5034        assertQueryParameter("foo:bar?some=some&param=val&else=else", "param", "val");
5035        assertQueryParameter("foo:bar?param=john%40doe.com", "param", "john@doe.com");
5036        assertQueryParameter("foo:bar?some_param=val", "param", null);
5037        assertQueryParameter("foo:bar?some_param=val1&param=val2", "param", "val2");
5038        assertQueryParameter("foo:bar?some_param=val1&param=", "param", "");
5039        assertQueryParameter("foo:bar?some_param=val1&param", "param", null);
5040        assertQueryParameter("foo:bar?some_param=val1&another_param=val2&param=val3",
5041                "param", "val3");
5042        assertQueryParameter("foo:bar?some_param=val1&param=val2&some_param=val3",
5043                "param", "val2");
5044        assertQueryParameter("foo:bar?param=val1&some_param=val2", "param", "val1");
5045        assertQueryParameter("foo:bar?p=val1&pp=val2", "p", "val1");
5046        assertQueryParameter("foo:bar?pp=val1&p=val2", "p", "val2");
5047        assertQueryParameter("foo:bar?ppp=val1&pp=val2&p=val3", "p", "val3");
5048        assertQueryParameter("foo:bar?ppp=val&", "p", null);
5049    }
5050
5051    public void testMissingAccountTypeParameter() {
5052        // Try querying for RawContacts only using ACCOUNT_NAME
5053        final Uri queryUri = RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(
5054                RawContacts.ACCOUNT_NAME, "lolwut").build();
5055        try {
5056            final Cursor cursor = mResolver.query(queryUri, null, null, null, null);
5057            fail("Able to query with incomplete account query parameters");
5058        } catch (IllegalArgumentException e) {
5059            // Expected behavior.
5060        }
5061    }
5062
5063    public void testInsertInconsistentAccountType() {
5064        // Try inserting RawContact with inconsistent Accounts
5065        final Account red = new Account("red", "red");
5066        final Account blue = new Account("blue", "blue");
5067
5068        final ContentValues values = new ContentValues();
5069        values.put(RawContacts.ACCOUNT_NAME, red.name);
5070        values.put(RawContacts.ACCOUNT_TYPE, red.type);
5071
5072        final Uri insertUri = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, blue);
5073        try {
5074            mResolver.insert(insertUri, values);
5075            fail("Able to insert RawContact with inconsistent account details");
5076        } catch (IllegalArgumentException e) {
5077            // Expected behavior.
5078        }
5079    }
5080
5081    public void testProviderStatusNoContactsNoAccounts() throws Exception {
5082        assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
5083    }
5084
5085    public void testProviderStatusOnlyLocalContacts() throws Exception {
5086        long rawContactId = createRawContact();
5087        assertProviderStatus(ProviderStatus.STATUS_NORMAL);
5088        mResolver.delete(
5089                ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), null, null);
5090        assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
5091    }
5092
5093    public void testProviderStatusWithAccounts() throws Exception {
5094        assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
5095        mActor.setAccounts(new Account[]{ACCOUNT_1});
5096        ((ContactsProvider2)getProvider()).onAccountsUpdated(new Account[]{ACCOUNT_1});
5097        assertProviderStatus(ProviderStatus.STATUS_NORMAL);
5098        mActor.setAccounts(new Account[0]);
5099        ((ContactsProvider2)getProvider()).onAccountsUpdated(new Account[0]);
5100        assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
5101    }
5102
5103    private void assertProviderStatus(int expectedProviderStatus) {
5104        Cursor cursor = mResolver.query(ProviderStatus.CONTENT_URI,
5105                new String[]{ProviderStatus.DATA1, ProviderStatus.STATUS}, null, null, null);
5106        assertTrue(cursor.moveToFirst());
5107        assertEquals(0, cursor.getLong(0));
5108        assertEquals(expectedProviderStatus, cursor.getInt(1));
5109        cursor.close();
5110    }
5111
5112    public void testProperties() throws Exception {
5113        ContactsProvider2 provider = (ContactsProvider2)getProvider();
5114        ContactsDatabaseHelper helper = (ContactsDatabaseHelper)provider.getDatabaseHelper();
5115        assertNull(helper.getProperty("non-existent", null));
5116        assertEquals("default", helper.getProperty("non-existent", "default"));
5117
5118        helper.setProperty("existent1", "string1");
5119        helper.setProperty("existent2", "string2");
5120        assertEquals("string1", helper.getProperty("existent1", "default"));
5121        assertEquals("string2", helper.getProperty("existent2", "default"));
5122        helper.setProperty("existent1", null);
5123        assertEquals("default", helper.getProperty("existent1", "default"));
5124    }
5125
5126    private class VCardTestUriCreator {
5127        private String mLookup1;
5128        private String mLookup2;
5129
5130        public VCardTestUriCreator(String lookup1, String lookup2) {
5131            super();
5132            mLookup1 = lookup1;
5133            mLookup2 = lookup2;
5134        }
5135
5136        public Uri getUri1() {
5137            return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1);
5138        }
5139
5140        public Uri getUri2() {
5141            return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2);
5142        }
5143
5144        public Uri getCombinedUri() {
5145            return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI,
5146                    Uri.encode(mLookup1 + ":" + mLookup2));
5147        }
5148    }
5149
5150    private VCardTestUriCreator createVCardTestContacts() {
5151        final long rawContactId1 = createRawContact(mAccount, RawContacts.SOURCE_ID, "4:12");
5152        insertStructuredName(rawContactId1, "John", "Doe");
5153
5154        final long rawContactId2 = createRawContact(mAccount, RawContacts.SOURCE_ID, "3:4%121");
5155        insertStructuredName(rawContactId2, "Jane", "Doh");
5156
5157        final long contactId1 = queryContactId(rawContactId1);
5158        final long contactId2 = queryContactId(rawContactId2);
5159        final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1);
5160        final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2);
5161        final String lookup1 =
5162            Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2));
5163        final String lookup2 =
5164            Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2));
5165        return new VCardTestUriCreator(lookup1, lookup2);
5166    }
5167
5168    public void testQueryMultiVCard() {
5169        // No need to create any contacts here, because the query for multiple vcards
5170        // does not go into the database at all
5171        Uri uri = Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, Uri.encode("123:456"));
5172        Cursor cursor = mResolver.query(uri, null, null, null, null);
5173        assertEquals(1, cursor.getCount());
5174        assertTrue(cursor.moveToFirst());
5175        assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
5176        String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
5177
5178        // The resulting name contains date and time. Ensure that before and after are correct
5179        assertTrue(filename.startsWith("vcards_"));
5180        assertTrue(filename.endsWith(".vcf"));
5181        cursor.close();
5182    }
5183
5184    public void testQueryFileSingleVCard() {
5185        final VCardTestUriCreator contacts = createVCardTestContacts();
5186
5187        {
5188            Cursor cursor = mResolver.query(contacts.getUri1(), null, null, null, null);
5189            assertEquals(1, cursor.getCount());
5190            assertTrue(cursor.moveToFirst());
5191            assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
5192            String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
5193            assertEquals("John Doe.vcf", filename);
5194            cursor.close();
5195        }
5196
5197        {
5198            Cursor cursor = mResolver.query(contacts.getUri2(), null, null, null, null);
5199            assertEquals(1, cursor.getCount());
5200            assertTrue(cursor.moveToFirst());
5201            assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
5202            String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
5203            assertEquals("Jane Doh.vcf", filename);
5204            cursor.close();
5205        }
5206    }
5207
5208    public void testQueryFileProfileVCard() {
5209        createBasicProfileContact(new ContentValues());
5210        Cursor cursor = mResolver.query(Profile.CONTENT_VCARD_URI, null, null, null, null);
5211        assertEquals(1, cursor.getCount());
5212        assertTrue(cursor.moveToFirst());
5213        assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
5214        String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
5215        assertEquals("Mia Prophyl.vcf", filename);
5216        cursor.close();
5217    }
5218
5219    public void testOpenAssetFileMultiVCard() throws IOException {
5220        final VCardTestUriCreator contacts = createVCardTestContacts();
5221
5222        final AssetFileDescriptor descriptor =
5223            mResolver.openAssetFileDescriptor(contacts.getCombinedUri(), "r");
5224        final FileInputStream inputStream = descriptor.createInputStream();
5225        String data = readToEnd(inputStream);
5226        inputStream.close();
5227        descriptor.close();
5228
5229        // Ensure that the resulting VCard has both contacts
5230        assertTrue(data.contains("N:Doe;John;;;"));
5231        assertTrue(data.contains("N:Doh;Jane;;;"));
5232    }
5233
5234    public void testOpenAssetFileSingleVCard() throws IOException {
5235        final VCardTestUriCreator contacts = createVCardTestContacts();
5236
5237        // Ensure that the right VCard is being created in each case
5238        {
5239            final AssetFileDescriptor descriptor =
5240                mResolver.openAssetFileDescriptor(contacts.getUri1(), "r");
5241            final FileInputStream inputStream = descriptor.createInputStream();
5242            final String data = readToEnd(inputStream);
5243            inputStream.close();
5244            descriptor.close();
5245
5246            assertTrue(data.contains("N:Doe;John;;;"));
5247            assertFalse(data.contains("N:Doh;Jane;;;"));
5248        }
5249
5250        {
5251            final AssetFileDescriptor descriptor =
5252                mResolver.openAssetFileDescriptor(contacts.getUri2(), "r");
5253            final FileInputStream inputStream = descriptor.createInputStream();
5254            final String data = readToEnd(inputStream);
5255            inputStream.close();
5256            descriptor.close();
5257
5258            assertFalse(data.contains("N:Doe;John;;;"));
5259            assertTrue(data.contains("N:Doh;Jane;;;"));
5260        }
5261    }
5262
5263    public void testAutoGroupMembership() {
5264        long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */);
5265        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
5266        long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false /* favorite */);
5267        long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, false/* favorite */);
5268        long r1 = createRawContact(mAccount);
5269        long r2 = createRawContact(mAccountTwo);
5270        long r3 = createRawContact(null);
5271
5272        Cursor c = queryGroupMemberships(mAccount);
5273        try {
5274            assertTrue(c.moveToNext());
5275            assertEquals(g1, c.getLong(0));
5276            assertEquals(r1, c.getLong(1));
5277            assertFalse(c.moveToNext());
5278        } finally {
5279            c.close();
5280        }
5281
5282        c = queryGroupMemberships(mAccountTwo);
5283        try {
5284            assertTrue(c.moveToNext());
5285            assertEquals(g3, c.getLong(0));
5286            assertEquals(r2, c.getLong(1));
5287            assertFalse(c.moveToNext());
5288        } finally {
5289            c.close();
5290        }
5291    }
5292
5293    public void testNoAutoAddMembershipAfterGroupCreation() {
5294        long r1 = createRawContact(mAccount);
5295        long r2 = createRawContact(mAccount);
5296        long r3 = createRawContact(mAccount);
5297        long r4 = createRawContact(mAccountTwo);
5298        long r5 = createRawContact(mAccountTwo);
5299        long r6 = createRawContact(null);
5300
5301        assertNoRowsAndClose(queryGroupMemberships(mAccount));
5302        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5303
5304        long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */);
5305        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
5306        long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false/* favorite */);
5307
5308        assertNoRowsAndClose(queryGroupMemberships(mAccount));
5309        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5310    }
5311
5312    // create some starred and non-starred contacts, some associated with account, some not
5313    // favorites group created
5314    // the starred contacts should be added to group
5315    // favorites group removed
5316    // no change to starred status
5317    public void testFavoritesMembershipAfterGroupCreation() {
5318        long r1 = createRawContact(mAccount, RawContacts.STARRED, "1");
5319        long r2 = createRawContact(mAccount);
5320        long r3 = createRawContact(mAccount, RawContacts.STARRED, "1");
5321        long r4 = createRawContact(mAccountTwo, RawContacts.STARRED, "1");
5322        long r5 = createRawContact(mAccountTwo);
5323        long r6 = createRawContact(null, RawContacts.STARRED, "1");
5324        long r7 = createRawContact(null);
5325
5326        assertNoRowsAndClose(queryGroupMemberships(mAccount));
5327        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5328
5329        long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
5330        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
5331        long g3 = createGroup(mAccountTwo, "g3", "t3", 0, false /* autoAdd */, false/* favorite */);
5332
5333        assertTrue(queryRawContactIsStarred(r1));
5334        assertFalse(queryRawContactIsStarred(r2));
5335        assertTrue(queryRawContactIsStarred(r3));
5336        assertTrue(queryRawContactIsStarred(r4));
5337        assertFalse(queryRawContactIsStarred(r5));
5338        assertTrue(queryRawContactIsStarred(r6));
5339        assertFalse(queryRawContactIsStarred(r7));
5340
5341        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5342        Cursor c = queryGroupMemberships(mAccount);
5343        try {
5344            assertTrue(c.moveToNext());
5345            assertEquals(g1, c.getLong(0));
5346            assertEquals(r1, c.getLong(1));
5347            assertTrue(c.moveToNext());
5348            assertEquals(g1, c.getLong(0));
5349            assertEquals(r3, c.getLong(1));
5350            assertFalse(c.moveToNext());
5351        } finally {
5352            c.close();
5353        }
5354
5355        updateItem(RawContacts.CONTENT_URI, r6,
5356                RawContacts.ACCOUNT_NAME, mAccount.name,
5357                RawContacts.ACCOUNT_TYPE, mAccount.type);
5358        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5359        c = queryGroupMemberships(mAccount);
5360        try {
5361            assertTrue(c.moveToNext());
5362            assertEquals(g1, c.getLong(0));
5363            assertEquals(r1, c.getLong(1));
5364            assertTrue(c.moveToNext());
5365            assertEquals(g1, c.getLong(0));
5366            assertEquals(r3, c.getLong(1));
5367            assertTrue(c.moveToNext());
5368            assertEquals(g1, c.getLong(0));
5369            assertEquals(r6, c.getLong(1));
5370            assertFalse(c.moveToNext());
5371        } finally {
5372            c.close();
5373        }
5374
5375        mResolver.delete(ContentUris.withAppendedId(Groups.CONTENT_URI, g1), null, null);
5376
5377        assertNoRowsAndClose(queryGroupMemberships(mAccount));
5378        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5379
5380        assertTrue(queryRawContactIsStarred(r1));
5381        assertFalse(queryRawContactIsStarred(r2));
5382        assertTrue(queryRawContactIsStarred(r3));
5383        assertTrue(queryRawContactIsStarred(r4));
5384        assertFalse(queryRawContactIsStarred(r5));
5385        assertTrue(queryRawContactIsStarred(r6));
5386        assertFalse(queryRawContactIsStarred(r7));
5387    }
5388
5389    public void testFavoritesGroupMembershipChangeAfterStarChange() {
5390        long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
5391        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */);
5392        long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */);
5393        long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */);
5394        long r1 = createRawContact(mAccount, RawContacts.STARRED, "1");
5395        long r2 = createRawContact(mAccount);
5396        long r3 = createRawContact(mAccountTwo);
5397
5398        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5399        Cursor c = queryGroupMemberships(mAccount);
5400        try {
5401            assertTrue(c.moveToNext());
5402            assertEquals(g1, c.getLong(0));
5403            assertEquals(r1, c.getLong(1));
5404            assertFalse(c.moveToNext());
5405        } finally {
5406            c.close();
5407        }
5408
5409        // remove the star from r1
5410        assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0"));
5411
5412        // Since no raw contacts are starred, there should be no group memberships.
5413        assertNoRowsAndClose(queryGroupMemberships(mAccount));
5414        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5415
5416        // mark r1 as starred
5417        assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "1"));
5418        // Now that r1 is starred it should have a membership in the one groups from mAccount
5419        // that is marked as a favorite.
5420        // There should be no memberships in mAccountTwo since it has no starred raw contacts.
5421        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5422        c = queryGroupMemberships(mAccount);
5423        try {
5424            assertTrue(c.moveToNext());
5425            assertEquals(g1, c.getLong(0));
5426            assertEquals(r1, c.getLong(1));
5427            assertFalse(c.moveToNext());
5428        } finally {
5429            c.close();
5430        }
5431
5432        // remove the star from r1
5433        assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0"));
5434        // Since no raw contacts are starred, there should be no group memberships.
5435        assertNoRowsAndClose(queryGroupMemberships(mAccount));
5436        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5437
5438        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(r1));
5439        assertNotNull(contactUri);
5440
5441        // mark r1 as starred via its contact lookup uri
5442        assertEquals(1, updateItem(contactUri, Contacts.STARRED, "1"));
5443        // Now that r1 is starred it should have a membership in the one groups from mAccount
5444        // that is marked as a favorite.
5445        // There should be no memberships in mAccountTwo since it has no starred raw contacts.
5446        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5447        c = queryGroupMemberships(mAccount);
5448        try {
5449            assertTrue(c.moveToNext());
5450            assertEquals(g1, c.getLong(0));
5451            assertEquals(r1, c.getLong(1));
5452            assertFalse(c.moveToNext());
5453        } finally {
5454            c.close();
5455        }
5456
5457        // remove the star from r1
5458        updateItem(contactUri, Contacts.STARRED, "0");
5459        // Since no raw contacts are starred, there should be no group memberships.
5460        assertNoRowsAndClose(queryGroupMemberships(mAccount));
5461        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5462    }
5463
5464    public void testStarChangedAfterGroupMembershipChange() {
5465        long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
5466        long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */);
5467        long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */);
5468        long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */);
5469        long r1 = createRawContact(mAccount);
5470        long r2 = createRawContact(mAccount);
5471        long r3 = createRawContact(mAccountTwo);
5472
5473        assertFalse(queryRawContactIsStarred(r1));
5474        assertFalse(queryRawContactIsStarred(r2));
5475        assertFalse(queryRawContactIsStarred(r3));
5476
5477        Cursor c;
5478
5479        // add r1 to one favorites group
5480        // r1's star should automatically be set
5481        // r1 should automatically be added to the other favorites group
5482        Uri urir1g1 = insertGroupMembership(r1, g1);
5483        assertTrue(queryRawContactIsStarred(r1));
5484        assertFalse(queryRawContactIsStarred(r2));
5485        assertFalse(queryRawContactIsStarred(r3));
5486        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5487        c = queryGroupMemberships(mAccount);
5488        try {
5489            assertTrue(c.moveToNext());
5490            assertEquals(g1, c.getLong(0));
5491            assertEquals(r1, c.getLong(1));
5492            assertFalse(c.moveToNext());
5493        } finally {
5494            c.close();
5495        }
5496
5497        // remove r1 from one favorites group
5498        mResolver.delete(urir1g1, null, null);
5499        // r1's star should no longer be set
5500        assertFalse(queryRawContactIsStarred(r1));
5501        assertFalse(queryRawContactIsStarred(r2));
5502        assertFalse(queryRawContactIsStarred(r3));
5503        // there should be no membership rows
5504        assertNoRowsAndClose(queryGroupMemberships(mAccount));
5505        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5506
5507        // add r3 to the one favorites group for that account
5508        // r3's star should automatically be set
5509        Uri urir3g4 = insertGroupMembership(r3, g4);
5510        assertFalse(queryRawContactIsStarred(r1));
5511        assertFalse(queryRawContactIsStarred(r2));
5512        assertTrue(queryRawContactIsStarred(r3));
5513        assertNoRowsAndClose(queryGroupMemberships(mAccount));
5514        c = queryGroupMemberships(mAccountTwo);
5515        try {
5516            assertTrue(c.moveToNext());
5517            assertEquals(g4, c.getLong(0));
5518            assertEquals(r3, c.getLong(1));
5519            assertFalse(c.moveToNext());
5520        } finally {
5521            c.close();
5522        }
5523
5524        // remove r3 from the favorites group
5525        mResolver.delete(urir3g4, null, null);
5526        // r3's star should automatically be cleared
5527        assertFalse(queryRawContactIsStarred(r1));
5528        assertFalse(queryRawContactIsStarred(r2));
5529        assertFalse(queryRawContactIsStarred(r3));
5530        assertNoRowsAndClose(queryGroupMemberships(mAccount));
5531        assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
5532    }
5533
5534    public void testReadOnlyRawContact() {
5535        long rawContactId = createRawContact();
5536        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
5537        storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first");
5538        storeValue(rawContactUri, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1);
5539
5540        storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "second");
5541        assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first");
5542
5543        Uri syncAdapterUri = rawContactUri.buildUpon()
5544                .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1")
5545                .build();
5546        storeValue(syncAdapterUri, RawContacts.CUSTOM_RINGTONE, "third");
5547        assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "third");
5548    }
5549
5550    public void testReadOnlyDataRow() {
5551        long rawContactId = createRawContact();
5552        Uri emailUri = insertEmail(rawContactId, "email");
5553        Uri phoneUri = insertPhoneNumber(rawContactId, "555-1111");
5554
5555        storeValue(emailUri, Data.IS_READ_ONLY, "1");
5556        storeValue(emailUri, Email.ADDRESS, "changed");
5557        storeValue(phoneUri, Phone.NUMBER, "555-2222");
5558        assertStoredValue(emailUri, Email.ADDRESS, "email");
5559        assertStoredValue(phoneUri, Phone.NUMBER, "555-2222");
5560
5561        Uri syncAdapterUri = emailUri.buildUpon()
5562                .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1")
5563                .build();
5564        storeValue(syncAdapterUri, Email.ADDRESS, "changed");
5565        assertStoredValue(emailUri, Email.ADDRESS, "changed");
5566    }
5567
5568    public void testContactWithReadOnlyRawContact() {
5569        long rawContactId1 = createRawContact();
5570        Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
5571        storeValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "first");
5572
5573        long rawContactId2 = createRawContact();
5574        Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
5575        storeValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second");
5576        storeValue(rawContactUri2, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1);
5577
5578        setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
5579                rawContactId1, rawContactId2);
5580
5581        long contactId = queryContactId(rawContactId1);
5582
5583        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
5584        storeValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt");
5585        assertStoredValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt");
5586        assertStoredValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "rt");
5587        assertStoredValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second");
5588    }
5589
5590    public void testNameParsingQuery() {
5591        Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name")
5592                .appendQueryParameter(StructuredName.DISPLAY_NAME, "Mr. John Q. Doe Jr.").build();
5593        Cursor cursor = mResolver.query(uri, null, null, null, null);
5594        ContentValues values = new ContentValues();
5595        values.put(StructuredName.DISPLAY_NAME, "Mr. John Q. Doe Jr.");
5596        values.put(StructuredName.PREFIX, "Mr.");
5597        values.put(StructuredName.GIVEN_NAME, "John");
5598        values.put(StructuredName.MIDDLE_NAME, "Q.");
5599        values.put(StructuredName.FAMILY_NAME, "Doe");
5600        values.put(StructuredName.SUFFIX, "Jr.");
5601        values.put(StructuredName.FULL_NAME_STYLE, FullNameStyle.WESTERN);
5602        assertTrue(cursor.moveToFirst());
5603        assertCursorValues(cursor, values);
5604        cursor.close();
5605    }
5606
5607    public void testNameConcatenationQuery() {
5608        Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name")
5609                .appendQueryParameter(StructuredName.PREFIX, "Mr")
5610                .appendQueryParameter(StructuredName.GIVEN_NAME, "John")
5611                .appendQueryParameter(StructuredName.MIDDLE_NAME, "Q.")
5612                .appendQueryParameter(StructuredName.FAMILY_NAME, "Doe")
5613                .appendQueryParameter(StructuredName.SUFFIX, "Jr.")
5614                .build();
5615        Cursor cursor = mResolver.query(uri, null, null, null, null);
5616        ContentValues values = new ContentValues();
5617        values.put(StructuredName.DISPLAY_NAME, "Mr John Q. Doe, Jr.");
5618        values.put(StructuredName.PREFIX, "Mr");
5619        values.put(StructuredName.GIVEN_NAME, "John");
5620        values.put(StructuredName.MIDDLE_NAME, "Q.");
5621        values.put(StructuredName.FAMILY_NAME, "Doe");
5622        values.put(StructuredName.SUFFIX, "Jr.");
5623        values.put(StructuredName.FULL_NAME_STYLE, FullNameStyle.WESTERN);
5624        assertTrue(cursor.moveToFirst());
5625        assertCursorValues(cursor, values);
5626        cursor.close();
5627    }
5628
5629    private Cursor queryGroupMemberships(Account account) {
5630        Cursor c = mResolver.query(maybeAddAccountQueryParameters(Data.CONTENT_URI, account),
5631                new String[]{GroupMembership.GROUP_ROW_ID, GroupMembership.RAW_CONTACT_ID},
5632                Data.MIMETYPE + "=?", new String[]{GroupMembership.CONTENT_ITEM_TYPE},
5633                GroupMembership.GROUP_SOURCE_ID);
5634        return c;
5635    }
5636
5637    private String readToEnd(FileInputStream inputStream) {
5638        try {
5639            System.out.println("DECLARED INPUT STREAM LENGTH: " + inputStream.available());
5640            int ch;
5641            StringBuilder stringBuilder = new StringBuilder();
5642            int index = 0;
5643            while (true) {
5644                ch = inputStream.read();
5645                System.out.println("READ CHARACTER: " + index + " " + ch);
5646                if (ch == -1) {
5647                    break;
5648                }
5649                stringBuilder.append((char)ch);
5650                index++;
5651            }
5652            return stringBuilder.toString();
5653        } catch (IOException e) {
5654            return null;
5655        }
5656    }
5657
5658    private void assertQueryParameter(String uriString, String parameter, String expectedValue) {
5659        assertEquals(expectedValue, ContactsProvider2.getQueryParameter(
5660                Uri.parse(uriString), parameter));
5661    }
5662
5663    private long createContact(ContentValues values, String firstName, String givenName,
5664            String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
5665            long groupId, int chatMode) {
5666        return createContact(values, firstName, givenName, phoneNumber, email, presenceStatus,
5667                timesContacted, starred, groupId, chatMode, false);
5668    }
5669
5670    private long createContact(ContentValues values, String firstName, String givenName,
5671            String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
5672            long groupId, int chatMode, boolean isUserProfile) {
5673        return queryContactId(createRawContact(values, firstName, givenName, phoneNumber, email,
5674                presenceStatus, timesContacted, starred, groupId, chatMode, isUserProfile));
5675    }
5676
5677    private long createRawContact(ContentValues values, String firstName, String givenName,
5678            String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
5679            long groupId, int chatMode) {
5680        long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus,
5681                timesContacted, starred, groupId, chatMode);
5682        insertStructuredName(rawContactId, firstName, givenName);
5683        return rawContactId;
5684    }
5685
5686    private long createRawContact(ContentValues values, String firstName, String givenName,
5687            String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
5688            long groupId, int chatMode, boolean isUserProfile) {
5689        long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus,
5690                timesContacted, starred, groupId, chatMode, isUserProfile);
5691        insertStructuredName(rawContactId, firstName, givenName);
5692        return rawContactId;
5693    }
5694
5695    private long createRawContact(ContentValues values, String phoneNumber, String email,
5696            int presenceStatus, int timesContacted, int starred, long groupId, int chatMode) {
5697        return createRawContact(values, phoneNumber, email, presenceStatus, timesContacted, starred,
5698                groupId, chatMode, false);
5699    }
5700
5701    private long createRawContact(ContentValues values, String phoneNumber, String email,
5702            int presenceStatus, int timesContacted, int starred, long groupId, int chatMode,
5703            boolean isUserProfile) {
5704        values.put(RawContacts.STARRED, starred);
5705        values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
5706        values.put(RawContacts.CUSTOM_RINGTONE, "beethoven5");
5707        values.put(RawContacts.TIMES_CONTACTED, timesContacted);
5708
5709        Uri insertionUri = isUserProfile
5710                ? Profile.CONTENT_RAW_CONTACTS_URI
5711                : RawContacts.CONTENT_URI;
5712        Uri rawContactUri = mResolver.insert(insertionUri, values);
5713        long rawContactId = ContentUris.parseId(rawContactUri);
5714        Uri photoUri = insertPhoto(rawContactId);
5715        long photoId = ContentUris.parseId(photoUri);
5716        values.put(Contacts.PHOTO_ID, photoId);
5717        insertPhoneNumber(rawContactId, phoneNumber);
5718        insertEmail(rawContactId, email);
5719
5720        insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, email, presenceStatus, "hacking",
5721                chatMode);
5722
5723        if (groupId != 0) {
5724            insertGroupMembership(rawContactId, groupId);
5725        }
5726
5727        return rawContactId;
5728    }
5729
5730    /**
5731     * Creates a raw contact with pre-set values under the user's profile.
5732     * @param profileValues Values to be used to create the entry (common values will be
5733     *     automatically populated in createRawContact()).
5734     * @return the raw contact ID that was created.
5735     */
5736    private long createBasicProfileContact(ContentValues profileValues) {
5737        long profileRawContactId = createRawContact(profileValues, "Mia", "Prophyl",
5738                "18005554411", "mia.prophyl@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
5739                StatusUpdates.CAPABILITY_HAS_CAMERA, true);
5740        profileValues.put(Contacts.DISPLAY_NAME, "Mia Prophyl");
5741        return profileRawContactId;
5742    }
5743
5744    /**
5745     * Creates a raw contact with pre-set values that is not under the user's profile.
5746     * @param nonProfileValues Values to be used to create the entry (common values will be
5747     *     automatically populated in createRawContact()).
5748     * @return the raw contact ID that was created.
5749     */
5750    private long createBasicNonProfileContact(ContentValues nonProfileValues) {
5751        long nonProfileRawContactId = createRawContact(nonProfileValues, "John", "Doe",
5752                "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
5753                StatusUpdates.CAPABILITY_HAS_CAMERA, false);
5754        nonProfileValues.put(Contacts.DISPLAY_NAME, "John Doe");
5755        return nonProfileRawContactId;
5756    }
5757
5758    private void putDataValues(ContentValues values, long rawContactId) {
5759        values.put(Data.RAW_CONTACT_ID, rawContactId);
5760        values.put(Data.MIMETYPE, "testmimetype");
5761        values.put(Data.RES_PACKAGE, "oldpackage");
5762        values.put(Data.IS_PRIMARY, 1);
5763        values.put(Data.IS_SUPER_PRIMARY, 1);
5764        values.put(Data.DATA1, "one");
5765        values.put(Data.DATA2, "two");
5766        values.put(Data.DATA3, "three");
5767        values.put(Data.DATA4, "four");
5768        values.put(Data.DATA5, "five");
5769        values.put(Data.DATA6, "six");
5770        values.put(Data.DATA7, "seven");
5771        values.put(Data.DATA8, "eight");
5772        values.put(Data.DATA9, "nine");
5773        values.put(Data.DATA10, "ten");
5774        values.put(Data.DATA11, "eleven");
5775        values.put(Data.DATA12, "twelve");
5776        values.put(Data.DATA13, "thirteen");
5777        values.put(Data.DATA14, "fourteen");
5778        values.put(Data.DATA15, "fifteen");
5779        values.put(Data.SYNC1, "sync1");
5780        values.put(Data.SYNC2, "sync2");
5781        values.put(Data.SYNC3, "sync3");
5782        values.put(Data.SYNC4, "sync4");
5783    }
5784
5785    /**
5786     * @param data1 email address or phone number
5787     * @param usageType One of {@link DataUsageFeedback#USAGE_TYPE}
5788     * @param values ContentValues for this feedback. Useful for incrementing
5789     * {Contacts#TIMES_CONTACTED} in the ContentValue. Can be null.
5790     */
5791    private void sendFeedback(String data1, String usageType, ContentValues values) {
5792        final long dataId = getStoredLongValue(Data.CONTENT_URI,
5793                Data.DATA1 + "=?", new String[] { data1 }, Data._ID);
5794        final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
5795                .appendPath(String.valueOf(dataId))
5796                .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, usageType)
5797                .build();
5798        assertNotSame(0, mResolver.update(feedbackUri, new ContentValues(), null, null));
5799        if (values != null && values.containsKey(Contacts.TIMES_CONTACTED)) {
5800            values.put(Contacts.TIMES_CONTACTED, values.getAsInteger(Contacts.TIMES_CONTACTED) + 1);
5801        }
5802    }
5803}
5804
5805