Fix malfunctioning bucketization logic

I82052953d5dad42ac171df29248ed25e9b4a2434 dropped the
logic so we need to re-introduce it correctly

As we don't have public API for updating LAST_TIME_USED
columns outside the provider, we need to let the unit test
call a method in ContactsProvider2 directly instead of
calling DataUsageStat API.

- fix the query
- make update logic package-private
- add a unit test for it
- introduce guava to include @VisibleForTesting

Bug: 4998821
Change-Id: I0b699bffffd42a13ab00ac335796687052efb67a
diff --git a/Android.mk b/Android.mk
index e3f60fc..634ef9f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -10,7 +10,7 @@
 
 LOCAL_JAVA_LIBRARIES := ext
 
-LOCAL_STATIC_JAVA_LIBRARIES += android-common com.android.vcard
+LOCAL_STATIC_JAVA_LIBRARIES += android-common com.android.vcard guava
 
 # The Emma tool analyzes code coverage when running unit tests on the
 # application. This configuration line selects which packages will be analyzed,
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index fa58025..6bbbfef 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -49,6 +49,7 @@
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 import com.google.android.collect.Sets;
+import com.google.common.annotations.VisibleForTesting;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
@@ -460,6 +461,9 @@
     // 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_USED =
+            "(strftime('%s', 'now') - " + DataUsageStatColumns.LAST_TIME_USED + "/1000)";
+
     /*
      * Sorting order for email address suggestions: first starred, then the rest.
      * second in_visible_group, then the rest.
@@ -472,9 +476,9 @@
     private static final String EMAIL_FILTER_SORT_ORDER =
         Contacts.STARRED + " DESC, "
         + Contacts.IN_VISIBLE_GROUP + " DESC, "
-        + "(CASE WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_CURRENT
+        + "(CASE WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_CURRENT
         + " THEN 0 "
-                + " WHEN " + DataUsageStatColumns.LAST_TIME_USED + " < " + EMAIL_FILTER_RECENT
+                + " WHEN " + TIME_SINCE_LAST_USED + " < " + EMAIL_FILTER_RECENT
         + " THEN 1 "
         + " ELSE 2 END), "
         + DataUsageStatColumns.TIMES_USED + " DESC, "
@@ -6863,8 +6867,9 @@
      *
      * @return the number of rows affected.
      */
-    private int updateDataUsageStat(
-            ArrayList<Long> dataIds, String type, long currentTimeMillis) {
+    @VisibleForTesting
+    /* package */ int updateDataUsageStat(
+            List<Long> dataIds, String type, long currentTimeMillis) {
         final int typeInt = sDataUsageTypeMap.get(type);
         final String where = DataUsageStatColumns.DATA_ID + " =? AND "
                 + DataUsageStatColumns.USAGE_TYPE_INT + " =?";
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index cdbf761..597b975 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
 import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
 import com.android.providers.contacts.tests.R;
 import com.google.android.collect.Lists;
@@ -31,6 +32,7 @@
 import android.content.EntityIterator;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.AggregationExceptions;
@@ -1244,6 +1246,71 @@
         assertStoredValuesOrderly(filterUri3, new ContentValues[] { v3, v1, v2 });
     }
 
+    /**
+     * Tests {@link DataUsageFeedback} correctly bucketize contacts using each
+     * {@link DataUsageStatColumns#LAST_TIME_USED}
+     */
+    public void testEmailFilterSortOrderWithOldHistory() {
+        long rawContactId1 = createRawContact();
+        long dataId1 = ContentUris.parseId(insertEmail(rawContactId1, "address1@email.com"));
+        long dataId2 = ContentUris.parseId(insertEmail(rawContactId1, "address2@email.com"));
+        long dataId3 = ContentUris.parseId(insertEmail(rawContactId1, "address3@email.com"));
+        long dataId4 = ContentUris.parseId(insertEmail(rawContactId1, "address4@email.com"));
+
+        Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
+
+        ContentValues v1 = new ContentValues();
+        v1.put(Email.ADDRESS, "address1@email.com");
+        ContentValues v2 = new ContentValues();
+        v2.put(Email.ADDRESS, "address2@email.com");
+        ContentValues v3 = new ContentValues();
+        v3.put(Email.ADDRESS, "address3@email.com");
+        ContentValues v4 = new ContentValues();
+        v4.put(Email.ADDRESS, "address4@email.com");
+
+        final ContactsProvider2 provider = (ContactsProvider2) getProvider();
+
+        long nowInMillis = System.currentTimeMillis();
+        long yesterdayInMillis = (nowInMillis - 24 * 60 * 60 * 1000);
+        long sevenDaysAgoInMillis = (nowInMillis - 7 * 24 * 60 * 60 * 1000);
+        long oneYearAgoInMillis = (nowInMillis - 365L * 24 * 60 * 60 * 1000);
+
+        // address4 is contacted just once yesterday.
+        provider.updateDataUsageStat(Arrays.asList(dataId4),
+                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, yesterdayInMillis);
+
+        // address3 is contacted twice 1 week ago.
+        provider.updateDataUsageStat(Arrays.asList(dataId3),
+                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis);
+        provider.updateDataUsageStat(Arrays.asList(dataId3),
+                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis);
+
+        // address2 is contacted three times 1 year ago.
+        provider.updateDataUsageStat(Arrays.asList(dataId2),
+                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
+        provider.updateDataUsageStat(Arrays.asList(dataId2),
+                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
+        provider.updateDataUsageStat(Arrays.asList(dataId2),
+                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
+
+        // auto-complete should prefer recently contacted methods
+        assertStoredValuesOrderly(filterUri1, new ContentValues[] { v4, v3, v2, v1 });
+
+        // Pretend address2 is contacted right now
+        provider.updateDataUsageStat(Arrays.asList(dataId2),
+                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis);
+
+        // Now address2 is the most recently used address
+        assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v4, v3, v1 });
+
+        // Pretend address1 is contacted right now
+        provider.updateDataUsageStat(Arrays.asList(dataId1),
+                DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis);
+
+        // address2 is preferred to address1 as address2 is used 4 times in total
+        assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v1, v4, v3 });
+    }
+
     public void testPostalsQuery() {
         long rawContactId = createRawContactWithName("Alice", "Nextore");
         Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");