Merge "DO NOT MERGE Add back Yenta experiment (1/2)" into ub-contactsdialer-i-dev
diff --git a/src-bind/com/android/contactsbind/ObjectFactory.java b/src-bind/com/android/contactsbind/ObjectFactory.java
index 5fbab3e..41e9df4 100644
--- a/src-bind/com/android/contactsbind/ObjectFactory.java
+++ b/src-bind/com/android/contactsbind/ObjectFactory.java
@@ -20,6 +20,7 @@
 
 import com.android.contacts.logging.Logger;
 import com.android.contacts.util.DeviceLocalAccountTypeFactory;
+import com.android.contactsbind.search.AutocompleteHelper;
 
 /**
  * Creates default bindings for overlays.
@@ -30,6 +31,10 @@
         return null;
     }
 
+    public static AutocompleteHelper getAutocompleteHelper(Context context) {
+        return null;
+    }
+
     public static DeviceLocalAccountTypeFactory getDeviceLocalAccountTypeFactory(Context context) {
         return new DeviceLocalAccountTypeFactory.Default(context);
     }
diff --git a/src-bind/com/android/contactsbind/search/AutocompleteHelper.java b/src-bind/com/android/contactsbind/search/AutocompleteHelper.java
index c37a828..ff0678c 100644
--- a/src-bind/com/android/contactsbind/search/AutocompleteHelper.java
+++ b/src-bind/com/android/contactsbind/search/AutocompleteHelper.java
@@ -25,7 +25,7 @@
         void onAutocompletesAvailable(Cursor cursor);
     }
 
-    private AutocompleteHelper() {
+    public AutocompleteHelper() {
     }
 
     public void setListener(Listener listener) {
diff --git a/src/com/android/contacts/Experiments.java b/src/com/android/contacts/Experiments.java
index 2485210..c371a3e 100644
--- a/src/com/android/contacts/Experiments.java
+++ b/src/com/android/contacts/Experiments.java
@@ -49,6 +49,16 @@
     public static final String PULL_TO_REFRESH_CANCEL_REFRESH_MILLIS =
             "PullToRefresh__cancel_refresh_millis";
 
+    /**
+     * Search study boolean indicating whether to inject yenta search results before CP2 results.
+     */
+    public static final String SEARCH_YENTA = "Search__yenta";
+
+    /**
+     * The time to wait for Yenta search results before giving up.
+     */
+    public static final String SEARCH_YENTA_TIMEOUT_MILLIS = "Search__yenta_timeout";
+
     private Experiments() {
     }
 }
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
index bc76331..d8344c6 100644
--- a/src/com/android/contacts/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -31,9 +31,11 @@
 import android.text.TextUtils;
 import android.view.View;
 
+import com.android.contacts.Experiments;
 import com.android.contacts.compat.ContactsCompat;
 import com.android.contacts.model.account.AccountWithDataSet;
 import com.android.contacts.preference.ContactsPreferences;
+import com.android.contactsbind.experiments.Flags;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -114,6 +116,13 @@
                 loader.setUri(builder.build());
                 loader.setProjection(getProjection(true));
                 sortOrder = STREQUENT_SORT;
+                if (Flags.getInstance().getBoolean(Experiments.SEARCH_YENTA)
+                        && loader instanceof FavoritesAndContactsLoader
+                        && directoryId == Directory.DEFAULT) {
+                    final FavoritesAndContactsLoader favoritesAndContactsLoader =
+                            (FavoritesAndContactsLoader) loader;
+                    favoritesAndContactsLoader.setAutocompleteQuery(query);
+                }
             }
         } else {
             final ContactListFilter filter = getFilter();
diff --git a/src/com/android/contacts/list/FavoritesAndContactsLoader.java b/src/com/android/contacts/list/FavoritesAndContactsLoader.java
index 54e5f3f..5868d29 100644
--- a/src/com/android/contacts/list/FavoritesAndContactsLoader.java
+++ b/src/com/android/contacts/list/FavoritesAndContactsLoader.java
@@ -22,24 +22,38 @@
 import android.database.sqlite.SQLiteException;
 import android.os.Bundle;
 import android.provider.ContactsContract.Contacts;
+import android.util.Log;
 
+import com.android.contacts.Experiments;
+import com.android.contactsbind.ObjectFactory;
+import com.android.contactsbind.experiments.Flags;
+import com.android.contactsbind.search.AutocompleteHelper;
 import com.google.common.collect.Lists;
 
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A loader for use in the default contact list, which will also query for favorite contacts
  * if configured to do so.
  */
-public class FavoritesAndContactsLoader extends CursorLoader {
+public class FavoritesAndContactsLoader extends CursorLoader implements
+        AutocompleteHelper.Listener {
 
     private boolean mLoadFavorites;
 
     private String[] mProjection;
 
+    private String mAutocompleteQuery;
+    private CountDownLatch mAutocompleteLatch = new CountDownLatch(1);
+    private Cursor mAutocompleteCursor;
+    private int mAutocompleteTimeout;
 
     public FavoritesAndContactsLoader(Context context) {
         super(context);
+        mAutocompleteTimeout = Flags.getInstance().getInteger(
+                Experiments.SEARCH_YENTA_TIMEOUT_MILLIS);
     }
 
     /** Whether to load favorites and merge results in before any other results. */
@@ -47,6 +61,10 @@
         mLoadFavorites = flag;
     }
 
+    public void setAutocompleteQuery(String autocompleteQuery) {
+        mAutocompleteQuery = autocompleteQuery;
+    }
+
     public void setProjection(String[] projection) {
         super.setProjection(projection);
         mProjection = projection;
@@ -58,8 +76,36 @@
         if (mLoadFavorites) {
             cursors.add(loadFavoritesContacts());
         }
-        final Cursor contactsCursor = loadContacts();
-        cursors.add(contactsCursor);
+
+        if (mAutocompleteQuery != null) {
+            final AutocompleteHelper autocompleteHelper =
+                    ObjectFactory.getAutocompleteHelper(getContext());
+            if (autocompleteHelper != null) {
+                autocompleteHelper.setListener(this);
+                autocompleteHelper.setProjection(mProjection);
+                autocompleteHelper.setQuery(mAutocompleteQuery);
+                try {
+                    if (!mAutocompleteLatch.await(mAutocompleteTimeout, TimeUnit.MILLISECONDS)) {
+                        logw("Timeout expired before receiving autocompletions");
+                    }
+                } catch (InterruptedException e) {
+                    logw("Interrupted while waiting for autocompletions");
+                }
+                if (mAutocompleteCursor != null) {
+                    cursors.add(mAutocompleteCursor);
+                    // TODO: exclude these results from the main loader results, see b/30742359
+                }
+            }
+        }
+
+        // TODO: if the autocomplete experiment in on, only show those results even if they're empty
+        final Cursor contactsCursor = mAutocompleteQuery == null ? loadContacts() : null;
+        if (mAutocompleteQuery == null) {
+            cursors.add(contactsCursor);
+        }
+        // Guard against passing an empty array to the MergeCursor constructor
+        if (cursors.isEmpty()) cursors.add(null);
+
         return new MergeCursor(cursors.toArray(new Cursor[cursors.size()])) {
             @Override
             public Bundle getExtras() {
@@ -93,4 +139,18 @@
                 Contacts.CONTENT_URI, mProjection, selection.toString(), new String[]{"1"},
                 getSortOrder());
     }
+
+    @Override
+    public void onAutocompletesAvailable(Cursor cursor) {
+        if (cursor != null && cursor.getCount() > 0) {
+            mAutocompleteCursor = cursor;
+            mAutocompleteLatch.countDown();
+        }
+    }
+
+    private static void logw(String message) {
+        if (Log.isLoggable(AutocompleteHelper.TAG, Log.WARN)) {
+            Log.w(AutocompleteHelper.TAG, message);
+        }
+    }
 }