summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
authorDaisuke Miyakawa <dmiyakawa@google.com>2011-06-06 17:30:07 -0700
committerDaisuke Miyakawa <dmiyakawa@google.com>2011-06-14 14:55:29 -0700
commit46abbb56764add30cb6e6506f55d8dededc88113 (patch)
treea5cf873e600a8c79503754e85e3025d9ddb02f12 /src/com
parent24c1d384b45a6d3c1cc959062a9d4308335fabbf (diff)
downloadpackages_providers_ContactsProvider-46abbb56764add30cb6e6506f55d8dededc88113.zip
packages_providers_ContactsProvider-46abbb56764add30cb6e6506f55d8dededc88113.tar.gz
packages_providers_ContactsProvider-46abbb56764add30cb6e6506f55d8dededc88113.tar.bz2
Introduce data usage table for per-method ranking.
- have a hidden table for per-method promotion - make filter API use it (phone, email only) - add a unit test - remove an old test using previous API Must be after: I602c0b83afca674904946f59bbdfc4dca07d46e4 Bug: 4371572 Change-Id: I82052953d5dad42ac171df29248ed25e9b4a2434
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/providers/contacts/ContactsDatabaseHelper.java77
-rw-r--r--src/com/android/providers/contacts/ContactsProvider2.java241
2 files changed, 283 insertions, 35 deletions
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index 5eb9243..473b30a 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -96,7 +96,7 @@ import java.util.Locale;
* 600-699 Ice Cream Sandwich
* </pre>
*/
- static final int DATABASE_VERSION = 600;
+ static final int DATABASE_VERSION = 601;
private static final String DATABASE_NAME = "contacts2.db";
private static final String DATABASE_PRESENCE = "presence_db";
@@ -124,6 +124,12 @@ import java.util.Locale;
public static final String DEFAULT_DIRECTORY = "default_directory";
public static final String SEARCH_INDEX = "search_index";
+ /**
+ * For {@link ContactsContract.DataUsageFeedback}. The table structure itself
+ * is not exposed outside.
+ */
+ public static final String DATA_USAGE_STAT = "data_usage_stat";
+
public static final String DATA_JOIN_MIMETYPES = "data "
+ "JOIN mimetypes ON (data.mimetype_id = mimetypes._id)";
@@ -507,6 +513,43 @@ import java.util.Locale;
public static final String TOKENS = "tokens";
}
+ /**
+ * Private table for calculating per-contact-method ranking.
+ */
+ public static final class DataUsageStatColumns {
+ /** type: INTEGER (long) */
+ public static final String _ID = "stat_id";
+ public static final String CONCRETE_ID = Tables.DATA_USAGE_STAT + "." + _ID;
+
+ /** type: INTEGER (long) */
+ public static final String DATA_ID = "data_id";
+ public static final String CONCRETE_DATA_ID = Tables.DATA_USAGE_STAT + "." + DATA_ID;
+
+ /** type: INTEGER (long) */
+ public static final String LAST_TIME_USED = "last_time_used";
+ public static final String CONCRETE_LAST_TIME_USED =
+ Tables.DATA_USAGE_STAT + "." + LAST_TIME_USED;
+
+ /** type: INTEGER */
+ public static final String TIMES_USED = "times_used";
+ public static final String CONCRETE_TIMES_USED =
+ Tables.DATA_USAGE_STAT + "." + TIMES_USED;
+
+ /** type: INTEGER */
+ public static final String USAGE_TYPE_INT = "usage_type";
+ public static final String CONCRETE_USAGE_TYPE =
+ Tables.DATA_USAGE_STAT + "." + USAGE_TYPE_INT;
+
+ /**
+ * Integer values for USAGE_TYPE.
+ *
+ * @see ContactsContract.DataUsageFeedback#USAGE_TYPE
+ */
+ public static final int USAGE_TYPE_INT_CALL = 0;
+ public static final int USAGE_TYPE_INT_LONG_TEXT = 1;
+ public static final int USAGE_TYPE_INT_SHORT_TEXT = 2;
+ }
+
/** In-memory cache of previously found MIME-type mappings */
private final HashMap<String, Long> mMimetypeCache = new HashMap<String, Long>();
/** In-memory cache of previously found package name mappings */
@@ -1075,6 +1118,21 @@ import java.util.Locale;
createDirectoriesTable(db);
createSearchIndexTable(db);
+ db.execSQL("CREATE TABLE " + Tables.DATA_USAGE_STAT + "(" +
+ DataUsageStatColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ DataUsageStatColumns.DATA_ID + " INTEGER NOT NULL, " +
+ DataUsageStatColumns.USAGE_TYPE_INT + " INTEGER NOT NULL DEFAULT 0, " +
+ DataUsageStatColumns.TIMES_USED + " INTEGER NOT NULL DEFAULT 0, " +
+ DataUsageStatColumns.LAST_TIME_USED + " INTERGER NOT NULL DEFAULT 0, " +
+ "FOREIGN KEY(" + DataUsageStatColumns.DATA_ID + ") REFERENCES "
+ + Tables.DATA + "(" + Data._ID + ")" +
+ ");");
+ db.execSQL("CREATE UNIQUE INDEX data_usage_stat_index ON " +
+ Tables.DATA_USAGE_STAT + " (" +
+ DataUsageStatColumns.DATA_ID + ", " +
+ DataUsageStatColumns.USAGE_TYPE_INT +
+ ");");
+
createContactsViews(db);
createGroupsView(db);
createContactsTriggers(db);
@@ -1859,6 +1917,11 @@ import java.util.Locale;
oldVersion = 600;
}
+ if (oldVersion < 601) {
+ upgradeToVersion601(db);
+ oldVersion = 601;
+ }
+
if (upgradeViewsAndTriggers) {
createContactsViews(db);
createGroupsView(db);
@@ -2911,6 +2974,18 @@ import java.util.Locale;
" (profile_raw_contact_id);");
}
+ private void upgradeToVersion601(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE data_usage_stat(" +
+ "stat_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ "data_id INTEGER NOT NULL, " +
+ "usage_type INTEGER NOT NULL DEFAULT 0, " +
+ "times_used INTEGER NOT NULL DEFAULT 0, " +
+ "last_time_used INTERGER NOT NULL DEFAULT 0, " +
+ "FOREIGN KEY(data_id) REFERENCES data(_id));");
+ db.execSQL("CREATE UNIQUE INDEX data_usage_stat_index ON " +
+ "data_usage_stat (data_id, usage_type)");
+ }
+
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 8073177..054f18a 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -34,6 +34,7 @@ import com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.SettingsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
@@ -111,6 +112,7 @@ import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.ProviderStatus;
import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.DataUsageFeedback;
import android.provider.ContactsContract.SearchSnippetColumns;
import android.provider.ContactsContract.Settings;
import android.provider.ContactsContract.StatusUpdates;
@@ -131,6 +133,7 @@ import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -287,6 +290,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
private static final int PROFILE_RAW_CONTACTS_ID_DATA = 19007;
private static final int PROFILE_RAW_CONTACTS_ID_ENTITIES = 19008;
+ private static final int DATA_USAGE_FEEDBACK_ID = 20001;
+
private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID =
RawContactsColumns.CONCRETE_ID + "=? AND "
+ GroupsColumns.CONCRETE_ACCOUNT_NAME
@@ -411,25 +416,27 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
// Recent contacts - those contacted within the last 30 days (in seconds)
private static final long EMAIL_FILTER_RECENT = 30 * 24 * 60 * 60;
- private static final String TIME_SINCE_LAST_CONTACTED =
- "(strftime('%s', 'now') - " + Contacts.LAST_TIME_CONTACTED + "/1000)";
-
/*
* Sorting order for email address suggestions: first starred, then the rest.
* Within the starred/unstarred groups - three buckets: very recently contacted, then fairly
* recently contacted, then the rest. Within each of the bucket - descending count
- * of times contacted. If all else fails, alphabetical. (Super)primary email
- * address is returned before other addresses for the same contact.
+ * of times contacted (both for data row and for contact row). If all else fails, alphabetical.
+ * (Super)primary email address is returned before other addresses for the same contact.
*/
private static final String EMAIL_FILTER_SORT_ORDER =
- "(CASE WHEN " + Contacts.STARRED + "=1 THEN 0 ELSE 1 END), "
- + "(CASE WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_CURRENT + " THEN 0 "
- + " WHEN " + TIME_SINCE_LAST_CONTACTED + " < " + EMAIL_FILTER_RECENT + " THEN 1 "
- + " ELSE 2 END),"
- + Contacts.TIMES_CONTACTED + " DESC, "
- + Contacts.DISPLAY_NAME + ", "
- + Data.CONTACT_ID + ", "
- + Data.IS_SUPER_PRIMARY + " DESC";
+ "(CASE WHEN " + Contacts.STARRED + "=1 THEN 0 ELSE 1 END), "
+ + "(CASE WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_CURRENT
+ + " THEN 0 "
+ + " WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_RECENT
+ + " THEN 1 "
+ + " ELSE 2 END), "
+ + DataUsageStatColumns.TIMES_USED + " DESC, "
+ + Contacts.DISPLAY_NAME + ", "
+ + Data.CONTACT_ID + ", "
+ + Data.IS_SUPER_PRIMARY + " DESC";
+
+ /** Currently same as {@link #EMAIL_FILTER_SORT_ORDER} */
+ private static final String PHONE_FILTER_SORT_ORDER = EMAIL_FILTER_SORT_ORDER;
/** Name lookup types used for contact filtering */
private static final String CONTACT_LOOKUP_NAME_TYPES =
@@ -866,6 +873,12 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
private Account mAccount;
+ /**
+ * Stores mapping from type Strings exposed via {@link DataUsageFeedback} to
+ * type integers in {@link DataUsageStatColumns}.
+ */
+ private static final Map<String, Integer> sDataUsageTypeMap;
+
static {
// Contacts URI matching table
final UriMatcher matcher = sUriMatcher;
@@ -918,6 +931,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
+ /** "*" is in CSV form with data ids ("123,456,789") */
+ matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
@@ -973,6 +988,14 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
PROFILE_RAW_CONTACTS_ID_DATA);
matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
PROFILE_RAW_CONTACTS_ID_ENTITIES);
+
+ HashMap<String, Integer> tmpTypeMap = new HashMap<String, Integer>();
+ tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_CALL, DataUsageStatColumns.USAGE_TYPE_INT_CALL);
+ tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_LONG_TEXT,
+ DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT);
+ tmpTypeMap.put(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT,
+ DataUsageStatColumns.USAGE_TYPE_INT_SHORT_TEXT);
+ sDataUsageTypeMap = Collections.unmodifiableMap(tmpTypeMap);
}
private static class DirectoryInfo {
@@ -2754,6 +2777,15 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
break;
}
+ case DATA_USAGE_FEEDBACK_ID: {
+ if (handleDataUsageFeedback(uri)) {
+ count = 1;
+ } else {
+ count = 0;
+ }
+ break;
+ }
+
default: {
mSyncToNetwork = true;
return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
@@ -3816,7 +3848,12 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
}
case PHONES_FILTER: {
- setTablesAndProjectionMapForData(qb, uri, projection, true);
+ String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
+ Integer typeInt = sDataUsageTypeMap.get(typeParam);
+ if (typeInt == null) {
+ typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
+ }
+ setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
if (uri.getPathSegments().size() > 2) {
String filterParam = uri.getLastPathSegment();
@@ -3864,7 +3901,12 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
}
groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
if (sortOrder == null) {
- sortOrder = Contacts.IN_VISIBLE_GROUP + " DESC, " + RawContacts.CONTACT_ID;
+ final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
+ if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
+ sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
+ } else {
+ sortOrder = PHONE_FILTER_SORT_ORDER;
+ }
}
break;
}
@@ -3896,14 +3938,14 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
}
case EMAILS_FILTER: {
- setTablesAndProjectionMapForData(qb, uri, projection, true);
+ String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
+ Integer typeInt = sDataUsageTypeMap.get(typeParam);
+ if (typeInt == null) {
+ typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
+ }
+ setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
String filterParam = null;
- String primaryAccountName =
- uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
- String primaryAccountType =
- uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
-
if (uri.getPathSegments().size() > 3) {
filterParam = uri.getLastPathSegment();
if (TextUtils.isEmpty(filterParam)) {
@@ -3945,19 +3987,9 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
}
groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
if (sortOrder == null) {
- // Addresses associated with primary account should be promoted.
- if (!TextUtils.isEmpty(primaryAccountName)) {
- StringBuilder sb2 = new StringBuilder();
- sb2.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
- DatabaseUtils.appendEscapedSQLString(sb2, primaryAccountName);
- if (!TextUtils.isEmpty(primaryAccountType)) {
- sb2.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
- DatabaseUtils.appendEscapedSQLString(sb2, primaryAccountType);
- }
- sb2.append(" THEN 0 ELSE 1 END), ");
- sb2.append(EMAIL_FILTER_SORT_ORDER);
-
- sortOrder = sb2.toString();
+ final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
+ if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
+ sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
} else {
sortOrder = EMAIL_FILTER_SORT_ORDER;
}
@@ -4931,6 +4963,15 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
String[] projection, boolean distinct) {
+ setTablesAndProjectionMapForData(qb, uri, projection, distinct, null);
+ }
+
+ /**
+ * @param usageType when non-null {@link Tables#DATA_USAGE_STAT} is joined with the specified
+ * type.
+ */
+ private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
+ String[] projection, boolean distinct, Integer usageType) {
StringBuilder sb = new StringBuilder();
sb.append(mDbHelper.getDataView(shouldExcludeRestrictedData(uri)));
sb.append(" data");
@@ -4940,6 +4981,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
+ if (usageType != null) {
+ appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
+ }
+
qb.setTables(sb.toString());
boolean useDistinct = distinct
@@ -5006,6 +5051,12 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
}
}
+ private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
+ sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
+ " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
+ " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
+ }
+
private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
String contactIdColumn) {
if (mDbHelper.isInProjection(projection,
@@ -5769,4 +5820,126 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
}
return sIsPhone;
}
+
+ private boolean handleDataUsageFeedback(Uri uri) {
+ final long currentTimeMillis = System.currentTimeMillis();
+ final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
+ final String[] ids = uri.getLastPathSegment().trim().split(",");
+ final ArrayList<Long> dataIds = new ArrayList<Long>();
+
+ for (String id : ids) {
+ dataIds.add(Long.valueOf(id));
+ }
+ final boolean successful;
+ if (TextUtils.isEmpty(usageType)) {
+ Log.w(TAG, "Method for data usage feedback isn't specified. Ignoring.");
+ successful = false;
+ } else {
+ successful = updateDataUsageStat(dataIds, usageType, currentTimeMillis) > 0;
+ }
+
+ // Handle old API. This doesn't affect the result of this entire method.
+ final String[] questionMarks = new String[ids.length];
+ Arrays.fill(questionMarks, "?");
+ final String where = Data._ID + " IN (" + TextUtils.join(",", questionMarks) + ")";
+ final Cursor cursor = mDb.query(
+ mDbHelper.getDataView(shouldExcludeRestrictedData(uri)),
+ new String[] { Data.CONTACT_ID },
+ where, ids, null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ mSelectionArgs1[0] = cursor.getString(0);
+ ContentValues values2 = new ContentValues();
+ values2.put(Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
+ mDb.update(Tables.CONTACTS, values2, Contacts._ID + "=?", mSelectionArgs1);
+ mDb.execSQL(UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
+ mDb.execSQL(UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ return successful;
+ }
+
+ /**
+ * Update {@link Tables#DATA_USAGE_STAT}.
+ *
+ * @return the number of rows affected.
+ */
+ private int updateDataUsageStat(
+ ArrayList<Long> dataIds, String type, long currentTimeMillis) {
+ final int typeInt = sDataUsageTypeMap.get(type);
+ final String where = DataUsageStatColumns.DATA_ID + " =? AND "
+ + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
+ final String[] columns =
+ new String[] { DataUsageStatColumns._ID, DataUsageStatColumns.TIMES_USED };
+ final ContentValues values = new ContentValues();
+ for (Long dataId : dataIds) {
+ final String[] args = new String[] { dataId.toString(), String.valueOf(typeInt) };
+ mDb.beginTransaction();
+ try {
+ final Cursor cursor = mDb.query(Tables.DATA_USAGE_STAT, columns, where, args,
+ null, null, null);
+ try {
+ if (cursor.getCount() > 0) {
+ if (!cursor.moveToFirst()) {
+ Log.e(TAG,
+ "moveToFirst() failed while getAccount() returned non-zero.");
+ } else {
+ values.clear();
+ values.put(DataUsageStatColumns.TIMES_USED, cursor.getInt(1) + 1);
+ values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
+ mDb.update(Tables.DATA_USAGE_STAT, values,
+ DataUsageStatColumns._ID + " =?",
+ new String[] { cursor.getString(0) });
+ }
+ } else {
+ values.clear();
+ values.put(DataUsageStatColumns.DATA_ID, dataId);
+ values.put(DataUsageStatColumns.USAGE_TYPE_INT, typeInt);
+ values.put(DataUsageStatColumns.TIMES_USED, 1);
+ values.put(DataUsageStatColumns.LAST_TIME_USED, currentTimeMillis);
+ mDb.insert(Tables.DATA_USAGE_STAT, null, values);
+ }
+ mDb.setTransactionSuccessful();
+ } finally {
+ cursor.close();
+ }
+ } finally {
+ mDb.endTransaction();
+ }
+ }
+
+ return dataIds.size();
+ }
+
+ /**
+ * Returns a sort order String for promoting data rows (email addresses, phone numbers, etc.)
+ * associated with a primary account. The primary account should be supplied from applications
+ * with {@link ContactsContract#PRIMARY_ACCOUNT_NAME} and
+ * {@link ContactsContract#PRIMARY_ACCOUNT_TYPE}. Null will be returned when the primary
+ * account isn't available.
+ */
+ private String getAccountPromotionSortOrder(Uri uri) {
+ final String primaryAccountName =
+ uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME);
+ final String primaryAccountType =
+ uri.getQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE);
+
+ // Data rows associated with primary account should be promoted.
+ if (!TextUtils.isEmpty(primaryAccountName)) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("(CASE WHEN " + RawContacts.ACCOUNT_NAME + "=");
+ DatabaseUtils.appendEscapedSQLString(sb, primaryAccountName);
+ if (!TextUtils.isEmpty(primaryAccountType)) {
+ sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
+ DatabaseUtils.appendEscapedSQLString(sb, primaryAccountType);
+ }
+ sb.append(" THEN 0 ELSE 1 END)");
+ return sb.toString();
+ } else {
+ return null;
+ }
+ }
}