blob: 2558c3609097d0fc21c12d9f783f418fa146d26f [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.provider.cts;
import static android.provider.cts.contacts.ContactUtil.newContentValues;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.AggregationExceptions;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PinnedPositions;
import android.provider.ContactsContract.RawContacts;
import android.provider.cts.contacts.CommonDatabaseUtils;
import android.provider.cts.contacts.ContactUtil;
import android.provider.cts.contacts.DatabaseAsserts;
import android.provider.cts.contacts.RawContactUtil;
import android.test.AndroidTestCase;
import android.util.Log;
import java.util.ArrayList;
/**
* CTS tests for {@link android.provider.ContactsContract.PinnedPositions} API
*/
public class ContactsContract_PinnedPositionsTest extends AndroidTestCase {
private static final String TAG = "ContactsContract_PinnedPositionsTest";
private ContentResolver mResolver;
@Override
protected void setUp() throws Exception {
super.setUp();
mResolver = getContext().getContentResolver();
}
/**
* Tests that the ContactsProvider automatically stars/unstars a pinned/unpinned contact if
* {@link PinnedPositions#STAR_WHEN_PINNING} boolean parameter is set to true, and that the
* values are correctly propogated to the contact's constituent raw contacts.
*/
public void testPinnedPositionsUpdate() {
final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
final int unpinned = PinnedPositions.UNPINNED;
assertValuesForContact(i1.mContactId,
newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
assertValuesForContact(i2.mContactId,
newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
assertValuesForContact(i3.mContactId,
newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
assertValuesForContact(i4.mContactId,
newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
assertValuesForRawContact(i4.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
final ArrayList<ContentProviderOperation> operations =
new ArrayList<ContentProviderOperation>();
operations.add(newPinningOperation(i1.mContactId, 1, true));
operations.add(newPinningOperation(i3.mContactId, 3, true));
operations.add(newPinningOperation(i4.mContactId, 2, false));
applyBatch(mResolver, operations);
assertValuesForContact(i1.mContactId,
newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 1));
assertValuesForContact(i2.mContactId,
newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
assertValuesForContact(i3.mContactId,
newContentValues(Contacts.PINNED, 3, Contacts.STARRED, 1));
assertValuesForContact(i4.mContactId,
newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 0));
// Make sure the values are propagated to raw contacts.
assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, 1));
assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, 3));
assertValuesForRawContact(i4.mRawContactId, newContentValues(RawContacts.PINNED, 2));
operations.clear();
// Now unpin the contact
operations.add(newPinningOperation(i3.mContactId, unpinned, false));
applyBatch(mResolver, operations);
assertValuesForContact(i1.mContactId,
newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 1));
assertValuesForContact(i2.mContactId,
newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
assertValuesForContact(i3.mContactId,
newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
assertValuesForContact(i4.mContactId,
newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 0));
assertValuesForRawContact(i1.mRawContactId,
newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 1));
assertValuesForRawContact(i2.mRawContactId,
newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
assertValuesForRawContact(i3.mRawContactId,
newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
assertValuesForRawContact(i4.mRawContactId,
newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 0));
ContactUtil.delete(mResolver, i1.mContactId);
ContactUtil.delete(mResolver, i2.mContactId);
ContactUtil.delete(mResolver, i3.mContactId);
ContactUtil.delete(mResolver, i4.mContactId);
}
/**
* Tests that pinned positions are correctly handled after the ContactsProvider aggregates
* and splits raw contacts.
*/
public void testPinnedPositionsAfterJoinAndSplit() {
final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
final DatabaseAsserts.ContactIdPair i5 = DatabaseAsserts.assertAndCreateContact(mResolver);
final DatabaseAsserts.ContactIdPair i6 = DatabaseAsserts.assertAndCreateContact(mResolver);
final ArrayList<ContentProviderOperation> operations =
new ArrayList<ContentProviderOperation>();
operations.add(newPinningOperation(i1.mContactId, 1, true));
operations.add(newPinningOperation(i2.mContactId, 2, true));
operations.add(newPinningOperation(i3.mContactId, 3, true));
operations.add(newPinningOperation(i5.mContactId, 5, true));
operations.add(newPinningOperation(i6.mContactId, 6, true));
applyBatch(mResolver, operations);
// Aggregate raw contact 1 and 4 together.
ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_TOGETHER,
i1.mRawContactId, i4.mRawContactId);
// If only one contact is pinned, the resulting contact should inherit the pinned position.
assertValuesForContact(i1.mContactId, newContentValues(Contacts.PINNED, 1));
assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
assertValuesForContact(i3.mContactId, newContentValues(Contacts.PINNED, 3));
assertValuesForContact(i5.mContactId, newContentValues(Contacts.PINNED, 5));
assertValuesForContact(i6.mContactId, newContentValues(Contacts.PINNED, 6));
assertValuesForRawContact(i1.mRawContactId,
newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 1));
assertValuesForRawContact(i2.mRawContactId,
newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 1));
assertValuesForRawContact(i3.mRawContactId,
newContentValues(RawContacts.PINNED, 3, RawContacts.STARRED, 1));
assertValuesForRawContact(i4.mRawContactId,
newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
0));
assertValuesForRawContact(i5.mRawContactId,
newContentValues(RawContacts.PINNED, 5, RawContacts.STARRED, 1));
assertValuesForRawContact(i6.mRawContactId,
newContentValues(RawContacts.PINNED, 6, RawContacts.STARRED, 1));
// Aggregate raw contact 2 and 3 together.
ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_TOGETHER,
i2.mRawContactId, i3.mRawContactId);
// If both raw contacts are pinned, the resulting contact should inherit the lower
// pinned position.
assertValuesForContact(i1.mContactId, newContentValues(Contacts.PINNED, 1));
assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
assertValuesForContact(i5.mContactId, newContentValues(Contacts.PINNED, 5));
assertValuesForContact(i6.mContactId, newContentValues(Contacts.PINNED, 6));
assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, 1));
assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, 2));
assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, 3));
assertValuesForRawContact(i4.mRawContactId,
newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED));
assertValuesForRawContact(i5.mRawContactId, newContentValues(RawContacts.PINNED, 5));
assertValuesForRawContact(i6.mRawContactId, newContentValues(RawContacts.PINNED, 6));
// Split the aggregated raw contacts.
ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_SEPARATE,
i1.mRawContactId, i4.mRawContactId);
// Raw contacts should be unpinned after being split, but still starred.
assertValuesForRawContact(i1.mRawContactId,
newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
1));
assertValuesForRawContact(i2.mRawContactId,
newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 1));
assertValuesForRawContact(i3.mRawContactId,
newContentValues(RawContacts.PINNED, 3, RawContacts.STARRED, 1));
assertValuesForRawContact(i4.mRawContactId,
newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
0));
assertValuesForRawContact(i5.mRawContactId,
newContentValues(RawContacts.PINNED, 5, RawContacts.STARRED, 1));
assertValuesForRawContact(i6.mRawContactId,
newContentValues(RawContacts.PINNED, 6, RawContacts.STARRED, 1));
// Now demote contact 5.
operations.clear();
operations.add(newPinningOperation(i5.mContactId, PinnedPositions.DEMOTED, false));
applyBatch(mResolver, operations);
// Get new contact Ids for contacts composing of raw contacts 1 and 4 because they have
// changed.
final long cId1 = RawContactUtil.queryContactIdByRawContactId(mResolver, i1.mRawContactId);
final long cId4 = RawContactUtil.queryContactIdByRawContactId(mResolver, i4.mRawContactId);
assertValuesForContact(cId1, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
assertValuesForContact(cId4, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
assertValuesForContact(i5.mContactId,
newContentValues(Contacts.PINNED, PinnedPositions.DEMOTED));
assertValuesForContact(i6.mContactId, newContentValues(Contacts.PINNED, 6));
// Aggregate contacts 5 and 6 together.
ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_TOGETHER,
i5.mRawContactId, i6.mRawContactId);
// The resulting contact should have a pinned value of 6.
assertValuesForContact(cId1, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
assertValuesForContact(cId4, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
assertValuesForContact(i5.mContactId, newContentValues(Contacts.PINNED, 6));
ContactUtil.delete(mResolver, cId1);
ContactUtil.delete(mResolver, i2.mContactId);
ContactUtil.delete(mResolver, cId4);
ContactUtil.delete(mResolver, i5.mContactId);
}
/**
* Tests that calling {@link PinnedPositions#UNDEMOTE_METHOD} with an illegal argument correctly
* throws an IllegalArgumentException.
*/
public void testPinnedPositionsDemoteIllegalArguments() {
try {
mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
null, null);
fail();
} catch (IllegalArgumentException expected) {
}
try {
mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
"1.1", null);
fail();
} catch (IllegalArgumentException expected) {
}
try {
mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
"NotANumber", null);
fail();
} catch (IllegalArgumentException expected) {
}
// Valid contact ID that does not correspond to an actual contact is silently ignored
mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD, "999",
null);
}
/**
* Tests that pinned positions are correctly handled for contacts that have been demoted
* or undemoted.
*/
public void testPinnedPositionsAfterDemoteAndUndemote() {
final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
// Pin contact 1 and demote contact 2
final ArrayList<ContentProviderOperation> operations =
new ArrayList<ContentProviderOperation>();
operations.add(newPinningOperation(i1.mContactId, 1, true));
operations.add(newPinningOperation(i2.mContactId, PinnedPositions.DEMOTED, false));
applyBatch(mResolver, operations);
assertValuesForContact(i1.mContactId,
newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 1));
assertValuesForContact(i2.mContactId,
newContentValues(Contacts.PINNED, PinnedPositions.DEMOTED, Contacts.STARRED, 0));
assertValuesForRawContact(i1.mRawContactId,
newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 1));
assertValuesForRawContact(i2.mRawContactId,
newContentValues(RawContacts.PINNED, PinnedPositions.DEMOTED, RawContacts.STARRED, 0));
// Now undemote both contacts.
PinnedPositions.undemote(mResolver, i1.mContactId);
PinnedPositions.undemote(mResolver, i2.mContactId);
// Contact 1 remains pinned at 0, while contact 2 becomes unpinned.
assertValuesForContact(i1.mContactId,
newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 1));
assertValuesForContact(i2.mContactId,
newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED, Contacts.STARRED, 0));
assertValuesForRawContact(i1.mRawContactId,
newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 1));
assertValuesForRawContact(i2.mRawContactId,
newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
0));
ContactUtil.delete(mResolver, i1.mContactId);
ContactUtil.delete(mResolver, i2.mContactId);
}
/**
* Verifies that the stored values for the contact that corresponds to the given contactId
* contain the exact same name-value pairs in the given ContentValues.
*
* @param contactId Id of a valid contact in the contacts database.
* @param contentValues A valid ContentValues object.
*/
private void assertValuesForContact(long contactId, ContentValues contentValues) {
DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, Contacts.CONTENT_URI.
buildUpon().appendEncodedPath(String.valueOf(contactId)).build(), contentValues);
}
/**
* Verifies that the stored values for the raw contact that corresponds to the given
* rawContactId contain the exact same name-value pairs in the given ContentValues.
*
* @param rawContactId Id of a valid contact in the contacts database
* @param contentValues A valid ContentValues object
*/
private void assertValuesForRawContact(long rawContactId, ContentValues contentValues) {
DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, RawContacts.CONTENT_URI.
buildUpon().appendEncodedPath(String.valueOf(rawContactId)).build(), contentValues);
}
/**
* Updates the contacts provider for a contact or raw contact corresponding to the given
* contact with key-value pairs as specified in the provided string parameters. Throws an
* exception if the number of provided string parameters is not zero or non-even.
*
* @param uri base URI that the provided ID will be appended onto, in order to creating the
* resulting URI
* @param id id of the contact of raw contact to perform the update for
* @param extras an even number of string parameters that correspond to name-value pairs
*
* @return the number of rows that were updated
*/
private int updateItemForContact(Uri uri, long id, String... extras) {
Uri itemUri = ContentUris.withAppendedId(uri, id);
return updateItemForUri(itemUri, extras);
}
/**
* Updates the contacts provider for the given YRU with key-value pairs as specified in the
* provided string parameters. Throws an exception if the number of provided string parameters
* is not zero or non-even.
*
* @param uri URI to perform the update for
* @param extras an even number of string parameters that correspond to name-value pairs
*
* @return the number of rows that were updated
*/
private int updateItemForUri(Uri uri, String... extras) {
ContentValues values = new ContentValues();
CommonDatabaseUtils.extrasVarArgsToValues(values, extras);
return mResolver.update(uri, values, null, null);
}
private ContentProviderOperation newPinningOperation(long id, int pinned, boolean star) {
final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_URI, String.valueOf(id));
final ContentValues values = new ContentValues();
values.put(Contacts.PINNED, pinned);
values.put(Contacts.STARRED, star ? 1 : 0);
return ContentProviderOperation.newUpdate(uri).withValues(values).build();
}
private static void applyBatch(ContentResolver resolver,
ArrayList<ContentProviderOperation> operations) {
try {
resolver.applyBatch(ContactsContract.AUTHORITY, operations);
} catch (OperationApplicationException e) {
Log.wtf(TAG, "ContentResolver batch operation failed.");
} catch (RemoteException e) {
Log.wtf(TAG, "Remote exception when performing batch operation.");
}
}
}