aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorke Lee <yorkelee@google.com>2013-07-30 20:42:05 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-07-30 20:42:05 +0000
commit96ade0922bec9e42c58ceeb1ee825e3d266e15a2 (patch)
treeb98b5bab3e1d3938ebb6d5722bdac08edb2d512c
parentc40e89125712d307f8cc0cc58d0227e96c251e23 (diff)
parent81fea08280784b319b936a3506788d595c6ce2ad (diff)
downloadContactsProvider-96ade0922bec9e42c58ceeb1ee825e3d266e15a2.tar.gz
Merge "Add pinning support in ContactsProvider"
-rw-r--r--src/com/android/providers/contacts/ContactsDatabaseHelper.java32
-rw-r--r--src/com/android/providers/contacts/ContactsProvider2.java83
-rw-r--r--src/com/android/providers/contacts/aggregation/ContactAggregator.java70
-rw-r--r--tests/src/com/android/providers/contacts/ContactsProvider2Test.java276
4 files changed, 446 insertions, 15 deletions
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index 34988b66..e0ac81d7 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -63,6 +63,7 @@ import android.provider.ContactsContract.FullNameStyle;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.PhoneticNameStyle;
import android.provider.ContactsContract.PhotoFiles;
+import android.provider.ContactsContract.PinnedPositions;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.Settings;
import android.provider.ContactsContract.StatusUpdates;
@@ -114,7 +115,7 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
* 800-899 Key Lime Pie
* </pre>
*/
- static final int DATABASE_VERSION = 801;
+ static final int DATABASE_VERSION = 802;
private static final String DATABASE_NAME = "contacts2.db";
private static final String DATABASE_PRESENCE = "presence_db";
@@ -361,6 +362,7 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
public static final String CONCRETE_LAST_TIME_CONTACTED = Tables.CONTACTS + "."
+ Contacts.LAST_TIME_CONTACTED;
public static final String CONCRETE_STARRED = Tables.CONTACTS + "." + Contacts.STARRED;
+ public static final String CONCRETE_PINNED = Tables.CONTACTS + "." + Contacts.PINNED;
public static final String CONCRETE_CUSTOM_RINGTONE = Tables.CONTACTS + "."
+ Contacts.CUSTOM_RINGTONE;
public static final String CONCRETE_SEND_TO_VOICEMAIL = Tables.CONTACTS + "."
@@ -407,6 +409,8 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
Tables.RAW_CONTACTS + "." + RawContacts.TIMES_CONTACTED;
public static final String CONCRETE_STARRED =
Tables.RAW_CONTACTS + "." + RawContacts.STARRED;
+ public static final String CONCRETE_PINNED =
+ Tables.RAW_CONTACTS + "." + RawContacts.PINNED;
public static final String DISPLAY_NAME = RawContacts.DISPLAY_NAME_PRIMARY;
public static final String DISPLAY_NAME_SOURCE = RawContacts.DISPLAY_NAME_SOURCE;
@@ -977,6 +981,7 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
Contacts.TIMES_CONTACTED + " INTEGER NOT NULL DEFAULT 0," +
Contacts.LAST_TIME_CONTACTED + " INTEGER," +
Contacts.STARRED + " INTEGER NOT NULL DEFAULT 0," +
+ Contacts.PINNED + " INTEGER NOT NULL DEFAULT " + PinnedPositions.UNPINNED + "," +
Contacts.HAS_PHONE_NUMBER + " INTEGER NOT NULL DEFAULT 0," +
Contacts.LOOKUP_KEY + " TEXT," +
ContactsColumns.LAST_STATUS_UPDATE_ID + " INTEGER REFERENCES data(_id)," +
@@ -1007,7 +1012,8 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
RawContacts.TIMES_CONTACTED + " INTEGER NOT NULL DEFAULT 0," +
RawContacts.LAST_TIME_CONTACTED + " INTEGER," +
RawContacts.STARRED + " INTEGER NOT NULL DEFAULT 0," +
- RawContacts.DISPLAY_NAME_PRIMARY + " TEXT," +
+ RawContacts.PINNED + " INTEGER NOT NULL DEFAULT " + PinnedPositions.UNPINNED +
+ "," + RawContacts.DISPLAY_NAME_PRIMARY + " TEXT," +
RawContacts.DISPLAY_NAME_ALTERNATIVE + " TEXT," +
RawContacts.DISPLAY_NAME_SOURCE + " INTEGER NOT NULL DEFAULT " +
DisplayNameSources.UNDEFINED + "," +
@@ -1634,7 +1640,9 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
+ ContactsColumns.CONCRETE_TIMES_CONTACTED
+ " AS " + RawContacts.TIMES_CONTACTED + ","
+ ContactsColumns.CONCRETE_STARRED
- + " AS " + RawContacts.STARRED;
+ + " AS " + RawContacts.STARRED + ","
+ + ContactsColumns.CONCRETE_PINNED
+ + " AS " + RawContacts.PINNED;
String contactNameColumns =
"name_raw_contact." + RawContacts.DISPLAY_NAME_SOURCE
@@ -1701,7 +1709,8 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
+ RawContacts.SEND_TO_VOICEMAIL + ","
+ RawContacts.LAST_TIME_CONTACTED + ","
+ RawContacts.TIMES_CONTACTED + ","
- + RawContacts.STARRED;
+ + RawContacts.STARRED + ","
+ + RawContacts.PINNED;
String rawContactsSelect = "SELECT "
+ RawContactsColumns.CONCRETE_ID + " AS " + RawContacts._ID + ","
@@ -1741,6 +1750,8 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
+ " AS " + Contacts.SEND_TO_VOICEMAIL + ", "
+ ContactsColumns.CONCRETE_STARRED
+ " AS " + Contacts.STARRED + ", "
+ + ContactsColumns.CONCRETE_PINNED
+ + " AS " + Contacts.PINNED + ", "
+ ContactsColumns.CONCRETE_TIMES_CONTACTED
+ " AS " + Contacts.TIMES_CONTACTED;
@@ -2494,6 +2505,12 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
oldVersion = 801;
}
+ if (oldVersion < 802) {
+ upgradeToVersion802(db);
+ upgradeViewsAndTriggers = true;
+ oldVersion = 802;
+ }
+
if (upgradeViewsAndTriggers) {
createContactsViews(db);
createGroupsView(db);
@@ -3988,6 +4005,13 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
db.execSQL("UPDATE calls SET presentation=4, number='' WHERE number='-3';");
}
+ private void upgradeToVersion802(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE contacts ADD pinned INTEGER NOT NULL DEFAULT " +
+ ContactsContract.PinnedPositions.UNPINNED + ";");
+ db.execSQL("ALTER TABLE raw_contacts ADD pinned INTEGER NOT NULL DEFAULT " +
+ ContactsContract.PinnedPositions.UNPINNED + ";");
+ }
+
public String extractHandleFromEmailAddress(String email) {
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
if (tokens.length == 0) {
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index cb134376..dc4826eb 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -92,6 +92,7 @@ import android.provider.ContactsContract.DisplayPhoto;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.PhotoFiles;
+import android.provider.ContactsContract.PinnedPositions;
import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.ProviderStatus;
import android.provider.ContactsContract.RawContacts;
@@ -179,6 +180,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -378,6 +380,8 @@ public class ContactsProvider2 extends AbstractContactsProvider
private static final int DELETED_CONTACTS = 23000;
private static final int DELETED_CONTACTS_ID = 23001;
+ private static final int PINNED_POSITION_UPDATE = 24001;
+
// Inserts into URIs in this map will direct to the profile database if the parent record's
// value (looked up from the ContentValues object with the key specified by the value in this
// map) is in the profile ID-space (see {@link ProfileDatabaseHelper#PROFILE_ID_SPACE}).
@@ -584,6 +588,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
.add(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE)
.add(ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE)
.add(Contacts.STARRED)
+ .add(Contacts.PINNED)
.add(Contacts.TIMES_CONTACTED)
.add(Contacts.HAS_PHONE_NUMBER)
.add(Contacts.CONTACT_LAST_UPDATED_TIMESTAMP)
@@ -785,6 +790,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
.add(RawContacts.CUSTOM_RINGTONE)
.add(RawContacts.SEND_TO_VOICEMAIL)
.add(RawContacts.STARRED)
+ .add(RawContacts.PINNED)
.add(RawContacts.AGGREGATION_MODE)
.add(RawContacts.RAW_CONTACT_IS_USER_PROFILE)
.addAll(sRawContactColumns)
@@ -1255,6 +1261,9 @@ public class ContactsProvider2 extends AbstractContactsProvider
matcher.addURI(ContactsContract.AUTHORITY, "deleted_contacts", DELETED_CONTACTS);
matcher.addURI(ContactsContract.AUTHORITY, "deleted_contacts/#", DELETED_CONTACTS_ID);
+
+ matcher.addURI(ContactsContract.AUTHORITY, "pinned_position_update",
+ PINNED_POSITION_UPDATE);
}
private static class DirectoryInfo {
@@ -4029,6 +4038,13 @@ public class ContactsProvider2 extends AbstractContactsProvider
break;
}
+ case PINNED_POSITION_UPDATE: {
+ final boolean forceStarWhenPinning = uri.getBooleanQueryParameter(
+ PinnedPositions.STAR_WHEN_PINNING, false);
+ count = handlePinningUpdate(values, forceStarWhenPinning);
+ break;
+ }
+
default: {
mSyncToNetwork = true;
return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
@@ -4357,6 +4373,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
flagIsSet(values, RawContacts.STARRED));
}
mAggregator.get().updateStarred(rawContactId);
+ mAggregator.get().updatePinned(rawContactId);
} else {
// if this raw contact is being associated with an account, then update the
// favorites group membership based on whether or not this contact is starred.
@@ -4489,6 +4506,8 @@ public class ContactsProvider2 extends AbstractContactsProvider
values, Contacts.TIMES_CONTACTED);
ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
values, Contacts.STARRED);
+ ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.PINNED,
+ values, Contacts.PINNED);
// Nothing to update - just return
if (mValues.size() == 0) {
@@ -4533,6 +4552,8 @@ public class ContactsProvider2 extends AbstractContactsProvider
values, Contacts.TIMES_CONTACTED);
ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
values, Contacts.STARRED);
+ ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.PINNED,
+ values, Contacts.PINNED);
mValues.put(Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
Clock.getInstance().currentTimeMillis());
@@ -8482,6 +8503,68 @@ public class ContactsProvider2 extends AbstractContactsProvider
// getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);
}
+ /**
+ * Handles pinning update information from clients.
+ *
+ * @param values ContentValues containing key-value pairs where keys correspond to
+ * the contactId for which to update the pinnedPosition, and the value is the actual
+ * pinned position (a positive integer).
+ * @return The number of contacts that had their pinned positions updated.
+ */
+ private int handlePinningUpdate(ContentValues values, boolean forceStarWhenPinning) {
+ if (values.size() == 0) return 0;
+ final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+ final String[] args;
+ if (forceStarWhenPinning) {
+ args = new String[3];
+ } else {
+ args = new String[2];
+ }
+
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append("UPDATE " + Tables.CONTACTS + " SET " + Contacts.PINNED + "=?2");
+ if (forceStarWhenPinning) {
+ sb.append("," + Contacts.STARRED + "=?3");
+ }
+ sb.append(" WHERE " + Contacts._ID + " =?1;");
+ final String contactSQL = sb.toString();
+
+ sb.setLength(0);
+ sb.append("UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.PINNED + "=?2");
+ if (forceStarWhenPinning) {
+ sb.append("," + RawContacts.STARRED + "=?3");
+ }
+ sb.append(" WHERE " + RawContacts.CONTACT_ID + " =?1;");
+ final String rawContactSQL = sb.toString();
+
+ int count = 0;
+ for (String id : values.keySet()) {
+ count++;
+ final long contactId;
+ try {
+ contactId = Integer.valueOf(id);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("contactId must be a positive integer. Found: "
+ + id);
+ }
+
+ final Integer pinnedPosition = values.getAsInteger(id);
+ if (pinnedPosition == null || pinnedPosition < 0) {
+ throw new IllegalArgumentException("Pinned position must be a positive integer.");
+ }
+ args[0] = String.valueOf(contactId);
+ args[1] = String.valueOf(pinnedPosition);
+ if (forceStarWhenPinning) {
+ args[2] = (pinnedPosition == PinnedPositions.UNPINNED ? "0" : "1");
+ }
+ db.execSQL(contactSQL, args);
+
+ db.execSQL(rawContactSQL, args);
+ }
+ return count;
+ }
+
private boolean handleDataUsageFeedback(Uri uri) {
final long currentTimeMillis = Clock.getInstance().currentTimeMillis();
final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
diff --git a/src/com/android/providers/contacts/aggregation/ContactAggregator.java b/src/com/android/providers/contacts/aggregation/ContactAggregator.java
index 6c53fa0f..db88c10b 100644
--- a/src/com/android/providers/contacts/aggregation/ContactAggregator.java
+++ b/src/com/android/providers/contacts/aggregation/ContactAggregator.java
@@ -33,6 +33,7 @@ import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.DisplayNameSources;
import android.provider.ContactsContract.FullNameStyle;
import android.provider.ContactsContract.PhotoFiles;
+import android.provider.ContactsContract.PinnedPositions;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.StatusUpdates;
import android.text.TextUtils;
@@ -152,11 +153,13 @@ public class ContactAggregator {
private SQLiteStatement mDisplayNameUpdate;
private SQLiteStatement mLookupKeyUpdate;
private SQLiteStatement mStarredUpdate;
+ private SQLiteStatement mPinnedUpdate;
private SQLiteStatement mContactIdAndMarkAggregatedUpdate;
private SQLiteStatement mContactIdUpdate;
private SQLiteStatement mMarkAggregatedUpdate;
private SQLiteStatement mContactUpdate;
private SQLiteStatement mContactInsert;
+ private SQLiteStatement mResetPinnedForRawContact;
private HashMap<Long, Integer> mRawContactsMarkedForAggregation = Maps.newHashMap();
@@ -333,6 +336,11 @@ public class ContactAggregator {
+ RawContacts.CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + " AND "
+ RawContacts.STARRED + "=1)" + " WHERE " + Contacts._ID + "=?");
+ mPinnedUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
+ + Contacts.PINNED + "=(SELECT MIN(" + RawContacts.PINNED + ") FROM "
+ + Tables.RAW_CONTACTS + " WHERE " + RawContacts.CONTACT_ID + "="
+ + ContactsColumns.CONCRETE_ID + ") WHERE " + Contacts._ID + "=?");
+
mContactIdAndMarkAggregatedUpdate = db.compileStatement(
"UPDATE " + Tables.RAW_CONTACTS +
" SET " + RawContacts.CONTACT_ID + "=?, "
@@ -357,6 +365,11 @@ public class ContactAggregator {
mContactUpdate = db.compileStatement(ContactReplaceSqlStatement.UPDATE_SQL);
mContactInsert = db.compileStatement(ContactReplaceSqlStatement.INSERT_SQL);
+ mResetPinnedForRawContact = db.compileStatement(
+ "UPDATE " + Tables.RAW_CONTACTS +
+ " SET " + RawContacts.PINNED + "=" + PinnedPositions.UNPINNED +
+ " WHERE " + RawContacts._ID + "=?");
+
mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
mMimeTypeIdIdentity = mDbHelper.getMimeTypeId(Identity.CONTENT_ITEM_TYPE);
mMimeTypeIdPhoto = mDbHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
@@ -989,10 +1002,13 @@ public class ContactAggregator {
}
/**
- * Creates a stand-alone Contact for the given raw contact ID.
+ * Creates a stand-alone Contact for the given raw contact ID. This is only called
+ * when splitting an existing merged contact into separate raw contacts.
*/
private void createNewContactForRawContact(
TransactionContext txContext, SQLiteDatabase db, long rawContactId) {
+ // All split contacts should automatically be unpinned.
+ unpinRawContact(rawContactId);
mSelectionArgs1[0] = String.valueOf(rawContactId);
computeAggregateData(db, mRawContactsQueryByRawContactId, mSelectionArgs1,
mContactInsert);
@@ -1097,6 +1113,11 @@ public class ContactAggregator {
mPresenceContactIdUpdate.execute();
}
+ private void unpinRawContact(long rawContactId) {
+ mResetPinnedForRawContact.bindLong(1, rawContactId);
+ mResetPinnedForRawContact.execute();
+ }
+
interface AggregateExceptionPrefetchQuery {
String TABLE = Tables.AGGREGATION_EXCEPTIONS;
@@ -1705,6 +1726,7 @@ public class ContactAggregator {
+ RawContacts.LAST_TIME_CONTACTED + ","
+ RawContacts.TIMES_CONTACTED + ","
+ RawContacts.STARRED + ","
+ + RawContacts.PINNED + ","
+ RawContacts.NAME_VERIFIED + ","
+ DataColumns.CONCRETE_ID + ","
+ DataColumns.CONCRETE_MIMETYPE_ID + ","
@@ -1740,11 +1762,12 @@ public class ContactAggregator {
int LAST_TIME_CONTACTED = 9;
int TIMES_CONTACTED = 10;
int STARRED = 11;
- int NAME_VERIFIED = 12;
- int DATA_ID = 13;
- int MIMETYPE_ID = 14;
- int IS_SUPER_PRIMARY = 15;
- int PHOTO_FILE_ID = 16;
+ int PINNED = 12;
+ int NAME_VERIFIED = 13;
+ int DATA_ID = 14;
+ int MIMETYPE_ID = 15;
+ int IS_SUPER_PRIMARY = 16;
+ int PHOTO_FILE_ID = 17;
}
private interface ContactReplaceSqlStatement {
@@ -1759,6 +1782,7 @@ public class ContactAggregator {
+ Contacts.LAST_TIME_CONTACTED + "=?, "
+ Contacts.TIMES_CONTACTED + "=?, "
+ Contacts.STARRED + "=?, "
+ + Contacts.PINNED + "=?, "
+ Contacts.HAS_PHONE_NUMBER + "=?, "
+ Contacts.LOOKUP_KEY + "=?, "
+ Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + "=? " +
@@ -1774,11 +1798,12 @@ public class ContactAggregator {
+ Contacts.LAST_TIME_CONTACTED + ", "
+ Contacts.TIMES_CONTACTED + ", "
+ Contacts.STARRED + ", "
+ + Contacts.PINNED + ", "
+ Contacts.HAS_PHONE_NUMBER + ", "
+ Contacts.LOOKUP_KEY + ", "
+ Contacts.CONTACT_LAST_UPDATED_TIMESTAMP
+ ") " +
- " VALUES (?,?,?,?,?,?,?,?,?,?,?)";
+ " VALUES (?,?,?,?,?,?,?,?,?,?,?,?)";
int NAME_RAW_CONTACT_ID = 1;
int PHOTO_ID = 2;
@@ -1788,10 +1813,11 @@ public class ContactAggregator {
int LAST_TIME_CONTACTED = 6;
int TIMES_CONTACTED = 7;
int STARRED = 8;
- int HAS_PHONE_NUMBER = 9;
- int LOOKUP_KEY = 10;
- int CONTACT_LAST_UPDATED_TIMESTAMP = 11;
- int CONTACT_ID = 12;
+ int PINNED = 9;
+ int HAS_PHONE_NUMBER = 10;
+ int LOOKUP_KEY = 11;
+ int CONTACT_LAST_UPDATED_TIMESTAMP = 12;
+ int CONTACT_ID = 13;
}
/**
@@ -1830,6 +1856,7 @@ public class ContactAggregator {
long contactLastTimeContacted = 0;
int contactTimesContacted = 0;
int contactStarred = 0;
+ int contactPinned = PinnedPositions.UNPINNED;
int hasPhoneNumber = 0;
StringBuilder lookupKey = new StringBuilder();
@@ -1886,6 +1913,11 @@ public class ContactAggregator {
contactStarred = 1;
}
+ // contactPinned should be the lowest value of its constituent raw contacts,
+ // excluding 0
+ final int rawContactPinned = c.getInt(RawContactsQuery.PINNED);
+ contactPinned = Math.min(contactPinned, rawContactPinned);
+
appendLookupKey(
lookupKey,
accountWithDataSet,
@@ -1951,6 +1983,8 @@ public class ContactAggregator {
contactTimesContacted);
statement.bindLong(ContactReplaceSqlStatement.STARRED,
contactStarred);
+ statement.bindLong(ContactReplaceSqlStatement.PINNED,
+ contactPinned);
statement.bindLong(ContactReplaceSqlStatement.HAS_PHONE_NUMBER,
hasPhoneNumber);
statement.bindString(ContactReplaceSqlStatement.LOOKUP_KEY,
@@ -2324,6 +2358,20 @@ public class ContactAggregator {
}
/**
+ * Execute {@link SQLiteStatement} that will update the
+ * {@link Contacts#PINNED} flag for the given {@link RawContacts#_ID}.
+ */
+ public void updatePinned(long rawContactId) {
+ long contactId = mDbHelper.getContactId(rawContactId);
+ if (contactId == 0) {
+ return;
+ }
+
+ mPinnedUpdate.bindLong(1, contactId);
+ mPinnedUpdate.execute();
+ }
+
+ /**
* Finds matching contacts and returns a cursor on those.
*/
public Cursor queryAggregationSuggestions(SQLiteQueryBuilder qb,
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 3c2b114d..1876589e 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -54,6 +54,7 @@ import android.provider.ContactsContract.FullNameStyle;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.PhoneticNameStyle;
+import android.provider.ContactsContract.PinnedPositions;
import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.ProviderStatus;
import android.provider.ContactsContract.RawContacts;
@@ -129,6 +130,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.LAST_TIME_CONTACTED,
Contacts.TIMES_CONTACTED,
Contacts.STARRED,
+ Contacts.PINNED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
Contacts.PHOTO_FILE_ID,
@@ -168,6 +170,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.LAST_TIME_CONTACTED,
Contacts.TIMES_CONTACTED,
Contacts.STARRED,
+ Contacts.PINNED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
Contacts.PHOTO_FILE_ID,
@@ -211,6 +214,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.LAST_TIME_CONTACTED,
Contacts.TIMES_CONTACTED,
Contacts.STARRED,
+ Contacts.PINNED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
Contacts.PHOTO_FILE_ID,
@@ -256,6 +260,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.LAST_TIME_CONTACTED,
Contacts.TIMES_CONTACTED,
Contacts.STARRED,
+ Contacts.PINNED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
Contacts.PHOTO_FILE_ID,
@@ -309,6 +314,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
RawContacts.CUSTOM_RINGTONE,
RawContacts.SEND_TO_VOICEMAIL,
RawContacts.STARRED,
+ RawContacts.PINNED,
RawContacts.AGGREGATION_MODE,
RawContacts.SYNC1,
RawContacts.SYNC2,
@@ -379,6 +385,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.LAST_TIME_CONTACTED,
Contacts.TIMES_CONTACTED,
Contacts.STARRED,
+ Contacts.PINNED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
Contacts.PHOTO_FILE_ID,
@@ -455,6 +462,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.LAST_TIME_CONTACTED,
Contacts.TIMES_CONTACTED,
Contacts.STARRED,
+ Contacts.PINNED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
Contacts.PHOTO_FILE_ID,
@@ -544,6 +552,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Contacts.LAST_TIME_CONTACTED,
Contacts.TIMES_CONTACTED,
Contacts.STARRED,
+ Contacts.PINNED,
Contacts.IN_VISIBLE_GROUP,
Contacts.PHOTO_ID,
Contacts.PHOTO_FILE_ID,
@@ -7708,10 +7717,277 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
return ids;
}
+
/**
* End delta api tests.
******************************************************/
+ /*******************************************************
+ * Pinning support tests
+ */
+ public void testPinnedPositionsUpdateForceStar() {
+ 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;
+
+ assertStoredValuesWithProjection(Contacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
+ cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
+ cv(Contacts._ID, i3.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
+ cv(Contacts._ID, i4.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0)
+ );
+
+ assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
+ cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, unpinned),
+ cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned),
+ cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, unpinned),
+ cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, unpinned)
+ );
+
+ final ContentValues values = cv(i1.mContactId, 1, i3.mContactId, 3, i4.mContactId, 2);
+ mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
+ .appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").build(),
+ values, null, null);
+
+ // Pinning a contact should automatically star it if we specified the boolean parameter
+
+ assertStoredValuesWithProjection(Contacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 1),
+ cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
+ cv(Contacts._ID, i3.mContactId, Contacts.PINNED, 3, Contacts.STARRED, 1),
+ cv(Contacts._ID, i4.mContactId, Contacts.PINNED, 2, Contacts.STARRED, 1)
+ );
+
+ // Make sure the values are propagated to raw contacts
+
+ assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
+ cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1),
+ cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned),
+ cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3),
+ cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, 2)
+ );
+
+ final ContentValues unpin = cv(i3.mContactId, unpinned);
+ mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
+ .appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").build(),
+ unpin, null, null);
+
+ // Unpinning a contact should automatically unstar it
+ assertStoredValuesWithProjection(Contacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 1),
+ cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
+ cv(Contacts._ID, i3.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
+ cv(Contacts._ID, i4.mContactId, Contacts.PINNED, 2, Contacts.STARRED, 1)
+ );
+
+ assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mRawContactId, RawContacts.PINNED, 1, RawContacts.STARRED, 1),
+ cv(Contacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned,
+ RawContacts.STARRED, 0),
+ cv(Contacts._ID, i3.mRawContactId, RawContacts.PINNED, unpinned,
+ RawContacts.STARRED, 0),
+ cv(Contacts._ID, i4.mRawContactId, RawContacts.PINNED, 2, RawContacts.STARRED, 1)
+ );
+ }
+
+ public void testPinnedPositionsUpdateDontForceStar() {
+ 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;
+
+ final ContentValues values = cv(i1.mContactId, 1, i3.mContactId, 3, i4.mContactId, 2);
+ mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, values, null, null);
+
+ // Pinning a contact should not automatically star it
+
+ assertStoredValuesWithProjection(Contacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 0),
+ cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
+ cv(Contacts._ID, i3.mContactId, Contacts.PINNED, 3, Contacts.STARRED, 0),
+ cv(Contacts._ID, i4.mContactId, Contacts.PINNED, 2, Contacts.STARRED, 0)
+ );
+
+ // Make sure the values are propagated to raw contacts
+
+ assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
+ cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1,
+ RawContacts.STARRED, 0),
+ cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned,
+ RawContacts.STARRED, 0),
+ cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3,
+ RawContacts.STARRED, 0),
+ cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, 2,
+ RawContacts.STARRED, 0)
+ );
+
+
+ // Manually star contact 3
+ assertEquals(1, updateItem(Contacts.CONTENT_URI, i3.mContactId, Contacts.STARRED, "1"));
+
+ // Check the third contact and raw contact is starred
+ assertStoredValuesWithProjection(Contacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 0),
+ cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
+ cv(Contacts._ID, i3.mContactId, Contacts.PINNED, 3, Contacts.STARRED, 1),
+ cv(Contacts._ID, i4.mContactId, Contacts.PINNED, 2, Contacts.STARRED, 0)
+ );
+
+ assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
+ cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1,
+ RawContacts.STARRED, 0),
+ cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned,
+ RawContacts.STARRED, 0),
+ cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3,
+ RawContacts.STARRED, 1),
+ cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, 2,
+ RawContacts.STARRED, 0)
+ );
+
+ final ContentValues unpin = cv(i3.mContactId, unpinned);
+
+ mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, unpin, null, null);
+
+ // Unpinning a contact should not automatically unstar it
+ assertStoredValuesWithProjection(Contacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 0),
+ cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
+ cv(Contacts._ID, i3.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 1),
+ cv(Contacts._ID, i4.mContactId, Contacts.PINNED, 2, Contacts.STARRED, 0)
+ );
+
+ assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mRawContactId, RawContacts.PINNED, 1, RawContacts.STARRED, 0),
+ cv(Contacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned,
+ RawContacts.STARRED, 0),
+ cv(Contacts._ID, i3.mRawContactId, RawContacts.PINNED, unpinned,
+ RawContacts.STARRED, 1),
+ cv(Contacts._ID, i4.mRawContactId, RawContacts.PINNED, 2, RawContacts.STARRED, 0)
+ );
+ }
+
+ public void testPinnedPositionsUpdateIllegalValues() {
+ 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);
+
+ assertStoredValuesWithProjection(Contacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED),
+ cv(Contacts._ID, i2.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED),
+ cv(Contacts._ID, i3.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED),
+ cv(Contacts._ID, i4.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED)
+ );
+
+ // negative number
+ final ContentValues values = cv(i1.mContactId, 1, i3.mContactId, 3, i4.mContactId, -2);
+ try {
+ mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, values, null, null);
+ fail("Pinned position must be a distinct(unrepeated) positive integer.");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // non-integer
+ final ContentValues values3 = cv(i1.mContactId, "1.1");
+ try {
+ mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, values, null, null);
+ fail("Pinned position must be a distinct(unrepeated) positive integer.");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // nothing should have been changed
+
+ assertStoredValuesWithProjection(Contacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED),
+ cv(Contacts._ID, i2.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED),
+ cv(Contacts._ID, i3.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED),
+ cv(Contacts._ID, i4.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED)
+ );
+
+ assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
+ cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED),
+ cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED),
+ cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED),
+ cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED)
+ );
+ }
+
+ 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 ContentValues values = cv(i1.mContactId, 1, i2.mContactId, 2, i3.mContactId, 3);
+ mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
+ .appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").build(),
+ values, null, null);
+
+ // aggregate raw contact 1 and 4 together.
+ setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, i1.mRawContactId,
+ i4.mRawContactId);
+
+ // If only one contact is pinned, the resulting contact should inherit the pinned position
+ assertStoredValuesWithProjection(Contacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1),
+ cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 2),
+ cv(Contacts._ID, i3.mContactId, Contacts.PINNED, 3)
+ );
+
+ assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
+ cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1,
+ RawContacts.STARRED, 1),
+ cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, 2,
+ RawContacts.STARRED, 1),
+ cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3,
+ RawContacts.STARRED, 1),
+ cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED,
+ RawContacts.STARRED, 0)
+ );
+
+ // aggregate raw contact 2 and 3 together.
+ setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, i2.mRawContactId,
+ i3.mRawContactId);
+
+ // If both raw contacts are pinned, the resulting contact should inherit the lower
+ // pinned position
+ assertStoredValuesWithProjection(Contacts.CONTENT_URI,
+ cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1),
+ cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 2)
+ );
+
+ assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
+ cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1),
+ cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, 2),
+ cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3),
+ cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED)
+ );
+
+ // split the aggregated raw contacts
+ setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, i1.mRawContactId,
+ i4.mRawContactId);
+
+ // raw contacts should be unpinned after being split, but still starred
+ assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
+ cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED,
+ RawContacts.STARRED, 1),
+ cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, 2,
+ RawContacts.STARRED, 1),
+ cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3,
+ RawContacts.STARRED, 1),
+ cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED,
+ RawContacts.STARRED, 0)
+ );
+ }
+
+ /**
+ * End pinning support tests
+ ******************************************************/
private Cursor queryGroupMemberships(Account account) {
Cursor c = mResolver.query(TestUtil.maybeAddAccountQueryParameters(Data.CONTENT_URI,