diff options
3 files changed, 120 insertions, 8 deletions
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 9e52f54..cf9068a 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -210,6 +210,8 @@ public class ContactsProvider2 extends AbstractContactsProvider */ private static final int DEFAULT_PREAUTHORIZED_URI_EXPIRATION = 5 * 60 * 1000; + private static final int USAGE_TYPE_ALL = -1; + /** * Random URI parameter that will be appended to preauthorized URIs for uniqueness. */ @@ -671,6 +673,11 @@ public class ContactsProvider2 extends AbstractContactsProvider .add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON) .build(); + private static final ProjectionMap sDataUsageColumns = ProjectionMap.builder() + .add(Data.TIMES_USED, Tables.DATA_USAGE_STAT + "." + Data.TIMES_USED) + .add(Data.LAST_TIME_USED, Tables.DATA_USAGE_STAT + "." + Data.LAST_TIME_USED) + .build(); + /** Contains just BaseColumns._COUNT */ private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder() .add(BaseColumns._COUNT, "COUNT(*)") @@ -824,6 +831,7 @@ public class ContactsProvider2 extends AbstractContactsProvider .addAll(sRawContactColumns) .addAll(sContactsColumns) .addAll(sContactPresenceColumns) + .addAll(sDataUsageColumns) .build(); /** Contains columns from the data view used for SIP address lookup. */ @@ -841,6 +849,7 @@ public class ContactsProvider2 extends AbstractContactsProvider .addAll(sDataPresenceColumns) .addAll(sContactsColumns) .addAll(sContactPresenceColumns) + .addAll(sDataUsageColumns) .build(); /** Contains columns from the data view used for SIP address lookup. */ @@ -5913,7 +5922,9 @@ public class ContactsProvider2 extends AbstractContactsProvider case DATA: case PROFILE_DATA: { - setTablesAndProjectionMapForData(qb, uri, projection, false); + final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE); + final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); + setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryParameter(Data.VISIBLE_CONTACTS_ONLY, false)) { qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY); @@ -7009,9 +7020,8 @@ public class ContactsProvider2 extends AbstractContactsProvider appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID); appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID); - if (usageType != null) { - appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID); - } + appendDataUsageStatJoin(sb, usageType == null ? USAGE_TYPE_ALL : usageType, + DataColumns.CONCRETE_ID); qb.setTables(sb.toString()); @@ -7108,9 +7118,29 @@ public class ContactsProvider2 extends AbstractContactsProvider } 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 + ")"); + if (usageType != USAGE_TYPE_ALL) { + sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT + + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="); + sb.append(dataIdColumn); + sb.append(" AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="); + sb.append(usageType); + sb.append(")"); + } else { + sb.append( + " LEFT OUTER JOIN " + + "(SELECT " + + DataUsageStatColumns.CONCRETE_DATA_ID + ", " + + "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED + + ") as " + DataUsageStatColumns.TIMES_USED + ", " + + "MAX(" + DataUsageStatColumns.CONCRETE_LAST_TIME_USED + + ") as " + DataUsageStatColumns.LAST_TIME_USED + + " FROM " + Tables.DATA_USAGE_STAT + " GROUP BY " + + DataUsageStatColumns.DATA_ID + ") as " + Tables.DATA_USAGE_STAT + ); + sb.append(" ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="); + sb.append(dataIdColumn); + sb.append(")"); + } } private void appendContactPresenceJoin(StringBuilder sb, String[] projection, diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java index 384d547..8e763f1 100644 --- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java @@ -17,6 +17,7 @@ package com.android.providers.contacts; import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY; +import static com.android.providers.contacts.TestUtils.cv; import android.accounts.Account; import android.content.ContentProvider; @@ -1112,6 +1113,22 @@ public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase { assertTrue(message.toString(), result); } + protected void assertCursorContains(Cursor cursor, ContentValues expectedValues) { + final StringBuilder message = new StringBuilder(); + boolean found = false; + cursor.moveToPosition(-1); + while (cursor.moveToNext()) { + message.setLength(0); + final int pos = cursor.getPosition(); + found = equalsWithExpectedValues(cursor, expectedValues, message); + if (found) { + break; + } + } + assertTrue("Expected values can not be found " + expectedValues + "," + message.toString(), + found); + } + protected void assertCursorValues(Cursor cursor, ContentValues... expectedValues) { StringBuilder message = new StringBuilder(); @@ -1179,6 +1196,25 @@ public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase { return true; } + private static final String[] DATA_USAGE_PROJECTION = + new String[] {Data.DATA1, Data.TIMES_USED, Data.LAST_TIME_USED}; + + protected void assertDataUsageCursorContains(Uri uri, String data1, int timesUsed, + int lastTimeUsed) { + final Cursor cursor = mResolver.query(uri, DATA_USAGE_PROJECTION, null, null, + null); + try { + assertCursorContains(cursor, + cv( + Data.DATA1, data1, + Data.TIMES_USED, timesUsed, + Data.LAST_TIME_USED, lastTimeUsed) + ); + } finally { + cursor.close(); + } + } + private String[] buildProjection(ContentValues values) { String[] projection = new String[values.size()]; Iterator<Entry<String, Object>> iter = values.valueSet().iterator(); diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index 84404b2..3f8b001 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -32,7 +32,7 @@ import android.os.AsyncTask; import android.provider.ContactsContract; import android.provider.ContactsContract.AggregationExceptions; import android.provider.ContactsContract.CommonDataKinds.Callable; -import android.provider.ContactsContract.CommonDataKinds.Contactables;; +import android.provider.ContactsContract.CommonDataKinds.Contactables; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.CommonDataKinds.GroupMembership; import android.provider.ContactsContract.CommonDataKinds.Im; @@ -343,6 +343,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Data.STATUS_RES_PACKAGE, Data.STATUS_LABEL, Data.STATUS_ICON, + Data.TIMES_USED, + Data.LAST_TIME_USED, RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, RawContacts.DATA_SET, @@ -424,6 +426,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Data.STATUS_RES_PACKAGE, Data.STATUS_LABEL, Data.STATUS_ICON, + Data.TIMES_USED, + Data.LAST_TIME_USED, RawContacts.RAW_CONTACT_IS_USER_PROFILE, Contacts._ID, Contacts.DISPLAY_NAME_PRIMARY, @@ -2514,6 +2518,48 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { // Now we have only 1 frequent. assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values1}); + + } + + public void testQueryDataUsageStat() { + ContentValues values1 = new ContentValues(); + final String email1 = "a@acme.com"; + final long cid1 = createContact(values1, "Noah", "Tever", "18004664411", + email1, StatusUpdates.OFFLINE, 0, 0, 0, 0); + + sMockClock.install(); + sMockClock.setCurrentTimeMillis(100); + + sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); + + assertDataUsageCursorContains(Data.CONTENT_URI, "a@acme.com", 1, 100); + + sMockClock.setCurrentTimeMillis(111); + sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); + + assertDataUsageCursorContains(Data.CONTENT_URI, "a@acme.com", 2, 111); + + sMockClock.setCurrentTimeMillis(123); + sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1); + + assertDataUsageCursorContains(Data.CONTENT_URI, "a@acme.com", 3, 123); + + final Uri dataUriWithUsageTypeLongText = Data.CONTENT_URI.buildUpon().appendQueryParameter( + DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_LONG_TEXT).build(); + + assertDataUsageCursorContains(dataUriWithUsageTypeLongText, "a@acme.com", 2, 111); + + sMockClock.setCurrentTimeMillis(200); + sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); + sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); + sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); + + assertDataUsageCursorContains(Data.CONTENT_URI, "a@acme.com", 6, 200); + + final Uri dataUriWithUsageTypeCall = Data.CONTENT_URI.buildUpon().appendQueryParameter( + DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_CALL).build(); + + assertDataUsageCursorContains(dataUriWithUsageTypeCall, "a@acme.com", 3, 200); } public void testQueryContactGroup() { |