Loading src-bind/com/android/contactsbind/ObjectFactory.java +5 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ package com.android.contactsbind; import com.android.contacts.common.logging.Logger; import com.android.contacts.common.preference.PreferenceManager; import com.android.contactsbind.search.AutocompleteHelper; import android.content.Context; Loading @@ -28,4 +29,8 @@ public class ObjectFactory { } public static PreferenceManager getPreferenceManager(Context context) { return null; } public static AutocompleteHelper getAutocompleteHelper(Context context) { return null; } } src-bind/com/android/contactsbind/search/AutocompleteHelper.java 0 → 100644 +39 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.contactsbind.search; import android.database.Cursor; public final class AutocompleteHelper { public static final String TAG = "Autocomplete"; public interface Listener { void onAutocompletesAvailable(Cursor cursor); } private AutocompleteHelper() { } public void setListener(Listener listener) { } public void setProjection(String[] projection) { } public void setQuery(String query) { } } No newline at end of file src/com/android/contacts/common/list/DefaultContactListAdapter.java +8 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.provider.ContactsContract.SearchSnippets; import android.text.TextUtils; import android.view.View; import com.android.contacts.common.Experiments; import com.android.contacts.common.compat.ContactsCompat; import com.android.contacts.common.model.account.AccountWithDataSet; import com.android.contacts.common.preference.ContactsPreferences; Loading Loading @@ -115,6 +116,13 @@ public class DefaultContactListAdapter extends ContactListAdapter { loader.setUri(builder.build()); loader.setProjection(getProjection(true)); sortOrder = STREQUENT_SORT; if (Flags.getInstance(getContext()).getBoolean(Experiments.SEARCH_YENTA) && loader instanceof FavoritesAndContactsLoader && directoryId == Directory.DEFAULT) { final FavoritesAndContactsLoader favoritesAndContactsLoader = (FavoritesAndContactsLoader) loader; favoritesAndContactsLoader.setAutocompleteQuery(query); } } } else { final ContactListFilter filter = getFilter(); Loading src/com/android/contacts/common/list/FavoritesAndContactsLoader.java +68 −45 Original line number Diff line number Diff line Loading @@ -19,29 +19,33 @@ import android.content.Context; import android.content.CursorLoader; import android.database.Cursor; import android.database.MergeCursor; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract.Contacts; import android.util.Log; import com.android.contactsbind.ObjectFactory; 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 static final int AUTOCOMPLETE_TIMEOUT_MS = 1000; private boolean mLoadFavorites; private String[] mProjection; private Uri mExtraUri; private String[] mExtraProjection; private String mExtraSelection; private String[] mExtraSelectionArgs; private boolean mMergeExtraContactsAfterPrimary; private String mAutocompleteQuery; private CountDownLatch mAutocompleteLatch = new CountDownLatch(1); private Cursor mAutocompleteCursor; public FavoritesAndContactsLoader(Context context) { super(context); Loading @@ -52,54 +56,51 @@ public class FavoritesAndContactsLoader extends CursorLoader { mLoadFavorites = flag; } public void setAutocompleteQuery(String autocompleteQuery) { mAutocompleteQuery = autocompleteQuery; } public void setProjection(String[] projection) { super.setProjection(projection); mProjection = projection; } /** Configure an extra query and merge results in before the primary results. */ public void setLoadExtraContactsFirst(Uri uri, String[] projection) { mExtraUri = uri; mExtraProjection = projection; mMergeExtraContactsAfterPrimary = false; } /** Configure an extra query and merge results in after the primary results. */ public void setLoadExtraContactsLast(Uri uri, String[] projection, String selection, String[] selectionArgs) { mExtraUri = uri; mExtraProjection = projection; mExtraSelection = selection; mExtraSelectionArgs = selectionArgs; mMergeExtraContactsAfterPrimary = true; } private boolean canLoadExtraContacts() { return mExtraUri != null && mExtraProjection != null; } @Override public Cursor loadInBackground() { List<Cursor> cursors = Lists.newArrayList(); if (mLoadFavorites) { cursors.add(loadFavoritesContacts()); } if (canLoadExtraContacts() && !mMergeExtraContactsAfterPrimary) { cursors.add(loadExtraContacts()); } // ContactsCursor.loadInBackground() can return null; MergeCursor // correctly handles null cursors. Cursor cursor = null; if (mAutocompleteQuery != null) { final AutocompleteHelper autocompleteHelper = ObjectFactory.getAutocompleteHelper(getContext()); if (autocompleteHelper != null) { autocompleteHelper.setListener(this); autocompleteHelper.setProjection(mProjection); autocompleteHelper.setQuery(mAutocompleteQuery); try { cursor = super.loadInBackground(); } catch (NullPointerException | SecurityException e) { // Ignore NPEs and SecurityExceptions thrown by providers if (!mAutocompleteLatch.await(AUTOCOMPLETE_TIMEOUT_MS, 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 } } final Cursor contactsCursor = cursor; } // 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); if (canLoadExtraContacts() && mMergeExtraContactsAfterPrimary) { cursors.add(loadExtraContacts()); } // 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() { Loading @@ -109,9 +110,15 @@ public class FavoritesAndContactsLoader extends CursorLoader { }; } private Cursor loadExtraContacts() { return getContext().getContentResolver().query( mExtraUri, mExtraProjection, mExtraSelection, mExtraSelectionArgs, null); private Cursor loadContacts() { // ContactsCursor.loadInBackground() can return null; MergeCursor // correctly handles null cursors. try { return super.loadInBackground(); } catch (NullPointerException | SecurityException e) { // Ignore NPEs and SecurityExceptions thrown by providers } return null; } private Cursor loadFavoritesContacts() { Loading @@ -119,4 +126,20 @@ public class FavoritesAndContactsLoader extends CursorLoader { Contacts.CONTENT_URI, mProjection, Contacts.STARRED + "=?", new String[]{"1"}, getSortOrder()); } @Override public void onAutocompletesAvailable(Cursor cursor) { if (cursor == null || cursor.getCount() == 0) { logw("Ignoring null or empty autocompletions"); } else { mAutocompleteCursor = cursor; mAutocompleteLatch.countDown(); } } private static void logw(String message) { if (Log.isLoggable(AutocompleteHelper.TAG, Log.WARN)) { Log.w(AutocompleteHelper.TAG, message); } } } Loading
src-bind/com/android/contactsbind/ObjectFactory.java +5 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ package com.android.contactsbind; import com.android.contacts.common.logging.Logger; import com.android.contacts.common.preference.PreferenceManager; import com.android.contactsbind.search.AutocompleteHelper; import android.content.Context; Loading @@ -28,4 +29,8 @@ public class ObjectFactory { } public static PreferenceManager getPreferenceManager(Context context) { return null; } public static AutocompleteHelper getAutocompleteHelper(Context context) { return null; } }
src-bind/com/android/contactsbind/search/AutocompleteHelper.java 0 → 100644 +39 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.contactsbind.search; import android.database.Cursor; public final class AutocompleteHelper { public static final String TAG = "Autocomplete"; public interface Listener { void onAutocompletesAvailable(Cursor cursor); } private AutocompleteHelper() { } public void setListener(Listener listener) { } public void setProjection(String[] projection) { } public void setQuery(String query) { } } No newline at end of file
src/com/android/contacts/common/list/DefaultContactListAdapter.java +8 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.provider.ContactsContract.SearchSnippets; import android.text.TextUtils; import android.view.View; import com.android.contacts.common.Experiments; import com.android.contacts.common.compat.ContactsCompat; import com.android.contacts.common.model.account.AccountWithDataSet; import com.android.contacts.common.preference.ContactsPreferences; Loading Loading @@ -115,6 +116,13 @@ public class DefaultContactListAdapter extends ContactListAdapter { loader.setUri(builder.build()); loader.setProjection(getProjection(true)); sortOrder = STREQUENT_SORT; if (Flags.getInstance(getContext()).getBoolean(Experiments.SEARCH_YENTA) && loader instanceof FavoritesAndContactsLoader && directoryId == Directory.DEFAULT) { final FavoritesAndContactsLoader favoritesAndContactsLoader = (FavoritesAndContactsLoader) loader; favoritesAndContactsLoader.setAutocompleteQuery(query); } } } else { final ContactListFilter filter = getFilter(); Loading
src/com/android/contacts/common/list/FavoritesAndContactsLoader.java +68 −45 Original line number Diff line number Diff line Loading @@ -19,29 +19,33 @@ import android.content.Context; import android.content.CursorLoader; import android.database.Cursor; import android.database.MergeCursor; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract.Contacts; import android.util.Log; import com.android.contactsbind.ObjectFactory; 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 static final int AUTOCOMPLETE_TIMEOUT_MS = 1000; private boolean mLoadFavorites; private String[] mProjection; private Uri mExtraUri; private String[] mExtraProjection; private String mExtraSelection; private String[] mExtraSelectionArgs; private boolean mMergeExtraContactsAfterPrimary; private String mAutocompleteQuery; private CountDownLatch mAutocompleteLatch = new CountDownLatch(1); private Cursor mAutocompleteCursor; public FavoritesAndContactsLoader(Context context) { super(context); Loading @@ -52,54 +56,51 @@ public class FavoritesAndContactsLoader extends CursorLoader { mLoadFavorites = flag; } public void setAutocompleteQuery(String autocompleteQuery) { mAutocompleteQuery = autocompleteQuery; } public void setProjection(String[] projection) { super.setProjection(projection); mProjection = projection; } /** Configure an extra query and merge results in before the primary results. */ public void setLoadExtraContactsFirst(Uri uri, String[] projection) { mExtraUri = uri; mExtraProjection = projection; mMergeExtraContactsAfterPrimary = false; } /** Configure an extra query and merge results in after the primary results. */ public void setLoadExtraContactsLast(Uri uri, String[] projection, String selection, String[] selectionArgs) { mExtraUri = uri; mExtraProjection = projection; mExtraSelection = selection; mExtraSelectionArgs = selectionArgs; mMergeExtraContactsAfterPrimary = true; } private boolean canLoadExtraContacts() { return mExtraUri != null && mExtraProjection != null; } @Override public Cursor loadInBackground() { List<Cursor> cursors = Lists.newArrayList(); if (mLoadFavorites) { cursors.add(loadFavoritesContacts()); } if (canLoadExtraContacts() && !mMergeExtraContactsAfterPrimary) { cursors.add(loadExtraContacts()); } // ContactsCursor.loadInBackground() can return null; MergeCursor // correctly handles null cursors. Cursor cursor = null; if (mAutocompleteQuery != null) { final AutocompleteHelper autocompleteHelper = ObjectFactory.getAutocompleteHelper(getContext()); if (autocompleteHelper != null) { autocompleteHelper.setListener(this); autocompleteHelper.setProjection(mProjection); autocompleteHelper.setQuery(mAutocompleteQuery); try { cursor = super.loadInBackground(); } catch (NullPointerException | SecurityException e) { // Ignore NPEs and SecurityExceptions thrown by providers if (!mAutocompleteLatch.await(AUTOCOMPLETE_TIMEOUT_MS, 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 } } final Cursor contactsCursor = cursor; } // 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); if (canLoadExtraContacts() && mMergeExtraContactsAfterPrimary) { cursors.add(loadExtraContacts()); } // 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() { Loading @@ -109,9 +110,15 @@ public class FavoritesAndContactsLoader extends CursorLoader { }; } private Cursor loadExtraContacts() { return getContext().getContentResolver().query( mExtraUri, mExtraProjection, mExtraSelection, mExtraSelectionArgs, null); private Cursor loadContacts() { // ContactsCursor.loadInBackground() can return null; MergeCursor // correctly handles null cursors. try { return super.loadInBackground(); } catch (NullPointerException | SecurityException e) { // Ignore NPEs and SecurityExceptions thrown by providers } return null; } private Cursor loadFavoritesContacts() { Loading @@ -119,4 +126,20 @@ public class FavoritesAndContactsLoader extends CursorLoader { Contacts.CONTENT_URI, mProjection, Contacts.STARRED + "=?", new String[]{"1"}, getSortOrder()); } @Override public void onAutocompletesAvailable(Cursor cursor) { if (cursor == null || cursor.getCount() == 0) { logw("Ignoring null or empty autocompletions"); } else { mAutocompleteCursor = cursor; mAutocompleteLatch.countDown(); } } private static void logw(String message) { if (Log.isLoggable(AutocompleteHelper.TAG, Log.WARN)) { Log.w(AutocompleteHelper.TAG, message); } } }