diff --git a/src/com/android/settings/search2/IntentSearchViewHolder.java b/src/com/android/settings/search2/IntentSearchViewHolder.java index f0cbc51391d..0596397b1b5 100644 --- a/src/com/android/settings/search2/IntentSearchViewHolder.java +++ b/src/com/android/settings/search2/IntentSearchViewHolder.java @@ -51,6 +51,7 @@ public class IntentSearchViewHolder extends SearchViewHolder { mMetricsFeatureProvider.action(v.getContext(), MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_RESULT, resultName, rank); + mSearchFeatureProvider.searchResultClicked(fragment.mQuery, result); fragment.startActivity(intent); }); } diff --git a/src/com/android/settings/search2/SearchFeatureProvider.java b/src/com/android/settings/search2/SearchFeatureProvider.java index d3dc24b026f..e77a332c575 100644 --- a/src/com/android/settings/search2/SearchFeatureProvider.java +++ b/src/com/android/settings/search2/SearchFeatureProvider.java @@ -22,6 +22,8 @@ import android.view.Menu; import android.view.View; import com.android.settings.dashboard.SiteMapManager; +import java.util.List; + /** * FeatureProvider for Settings Search */ @@ -89,5 +91,29 @@ public interface SearchFeatureProvider { default void hideFeedbackButton() { } + /** + * Ranks search results based on the input query. + * + * @param query input user query + * @param searchResults list of search results to be ranked + */ + default void rankSearchResults(String query, List searchResults) { + } + + /** + * Notify that a search result is clicked. + * + * @param query input user query + * @param searchResult clicked result + */ + default void searchResultClicked(String query, SearchResult searchResult) { + } + + /** + * @return true to enable search ranking. + */ + default boolean isSmartSearchRankingEnabled(Context context) { + return false; + } } diff --git a/src/com/android/settings/search2/SearchFragment.java b/src/com/android/settings/search2/SearchFragment.java index 8e1e1b4571d..a8d219c771a 100644 --- a/src/com/android/settings/search2/SearchFragment.java +++ b/src/com/android/settings/search2/SearchFragment.java @@ -117,7 +117,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O super.onCreate(savedInstanceState); setHasOptionsMenu(true); final LoaderManager loaderManager = getLoaderManager(); - mSearchAdapter = new SearchResultsAdapter(this); + mSearchAdapter = new SearchResultsAdapter(this, mSearchFeatureProvider); mSavedQueryController = new SavedQueryController( getContext(), loaderManager, mSearchAdapter); mSearchFeatureProvider.initFeedbackButton(); @@ -251,7 +251,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O if (mUnfinishedLoadersCount.decrementAndGet() != 0) { return; } - final int resultCount = mSearchAdapter.displaySearchResults(); + final int resultCount = mSearchAdapter.displaySearchResults(mQuery); mNoResultsView.setVisibility(resultCount == 0 ? View.VISIBLE : View.GONE); mSearchFeatureProvider.showFeedbackButton(this, getView()); } diff --git a/src/com/android/settings/search2/SearchResultsAdapter.java b/src/com/android/settings/search2/SearchResultsAdapter.java index 6ff68b17bd6..d0ea5bf3fcd 100644 --- a/src/com/android/settings/search2/SearchResultsAdapter.java +++ b/src/com/android/settings/search2/SearchResultsAdapter.java @@ -40,11 +40,14 @@ public class SearchResultsAdapter extends RecyclerView.Adapter private final List mSearchResults; private final SearchFragment mFragment; private Map> mResultsMap; + private final SearchFeatureProvider mSearchFeatureProvider; - public SearchResultsAdapter(SearchFragment fragment) { + public SearchResultsAdapter(SearchFragment fragment, + SearchFeatureProvider searchFeatureProvider) { mFragment = fragment; mSearchResults = new ArrayList<>(); mResultsMap = new ArrayMap<>(); + mSearchFeatureProvider = searchFeatureProvider; setHasStableIds(true); } @@ -119,9 +122,10 @@ public class SearchResultsAdapter extends RecyclerView.Adapter * Merge the results from each of the loaders into one list for the adapter. * Prioritizes results from the local database over installed apps. * + * @param query user query corresponding to these results * @return Number of matched results */ - public int displaySearchResults() { + public int displaySearchResults(String query) { final List databaseResults = mResultsMap .get(DatabaseResultLoader.class.getName()); final List installedAppResults = mResultsMap @@ -151,6 +155,12 @@ public class SearchResultsAdapter extends RecyclerView.Adapter results.add(installedAppResults.get(appIndex++)); } + if (mSearchFeatureProvider + .isSmartSearchRankingEnabled(mFragment.getContext().getApplicationContext())) { + // TODO: run this in parallel to loading the results if takes too long + mSearchFeatureProvider.rankSearchResults(query, results); + } + mSearchResults.addAll(results); notifyDataSetChanged(); diff --git a/src/com/android/settings/search2/SearchViewHolder.java b/src/com/android/settings/search2/SearchViewHolder.java index 123a602c5c4..1439833c663 100644 --- a/src/com/android/settings/search2/SearchViewHolder.java +++ b/src/com/android/settings/search2/SearchViewHolder.java @@ -40,11 +40,14 @@ public abstract class SearchViewHolder extends RecyclerView.ViewHolder { public final ImageView iconView; protected final MetricsFeatureProvider mMetricsFeatureProvider; + protected final SearchFeatureProvider mSearchFeatureProvider; public SearchViewHolder(View view) { super(view); - mMetricsFeatureProvider = FeatureFactory.getFactory(view.getContext()) - .getMetricsFeatureProvider(); + final FeatureFactory featureFactory = FeatureFactory + .getFactory(view.getContext().getApplicationContext()); + mMetricsFeatureProvider = featureFactory.getMetricsFeatureProvider(); + mSearchFeatureProvider = featureFactory.getSearchFeatureProvider(); titleView = view.findViewById(android.R.id.title); summaryView = view.findViewById(android.R.id.summary); iconView = view.findViewById(android.R.id.icon); diff --git a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java index 647d68c35a5..f5a29ce4adc 100644 --- a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java +++ b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java @@ -34,6 +34,7 @@ import com.android.settings.search2.InstalledAppResultLoader; import com.android.settings.search2.IntentPayload; import com.android.settings.search2.IntentSearchViewHolder; import com.android.settings.search2.ResultPayload; +import com.android.settings.search2.SearchFeatureProvider; import com.android.settings.search2.SearchFragment; import com.android.settings.search2.SearchResult; import com.android.settings.search2.SearchResult.Builder; @@ -48,6 +49,14 @@ import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.annotation.Config; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.util.ArrayList; import java.util.List; @@ -59,6 +68,10 @@ public class SearchResultsAdapterTest { @Mock private SearchFragment mFragment; + @Mock + private SearchFeatureProvider mSearchFeatureProvider; + @Mock + private Context mMockContext; private SearchResultsAdapter mAdapter; private Context mContext; private String mLoaderClassName; @@ -67,8 +80,10 @@ public class SearchResultsAdapterTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = Robolectric.buildActivity(Activity.class).get(); - mAdapter = new SearchResultsAdapter(mFragment); + mAdapter = new SearchResultsAdapter(mFragment, mSearchFeatureProvider); mLoaderClassName = DatabaseResultLoader.class.getName(); + when(mFragment.getContext()).thenReturn(mMockContext); + when(mMockContext.getApplicationContext()).thenReturn(mContext); } @Test @@ -81,7 +96,7 @@ public class SearchResultsAdapterTest { public void testSingleSourceMerge_ExactCopyReturned() { ArrayList intentResults = getIntentSampleResults(); mAdapter.addSearchResults(intentResults, mLoaderClassName); - mAdapter.displaySearchResults(); + mAdapter.displaySearchResults(""); List updatedResults = mAdapter.getSearchResults(); assertThat(updatedResults).containsAllIn(intentResults); @@ -109,7 +124,7 @@ public class SearchResultsAdapterTest { InstalledAppResultLoader.class.getName()); mAdapter.addSearchResults(getDummyDbResults(), DatabaseResultLoader.class.getName()); - int count = mAdapter.displaySearchResults(); + int count = mAdapter.displaySearchResults(""); List results = mAdapter.getSearchResults(); assertThat(results.get(0).title).isEqualTo("alpha"); @@ -121,6 +136,22 @@ public class SearchResultsAdapterTest { assertThat(count).isEqualTo(6); } + @Test + public void testDisplayResults_ShouldNotRunSmartRankingIfDisabled() { + when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())) + .thenReturn(false); + mAdapter.displaySearchResults(""); + verify(mSearchFeatureProvider, never()).rankSearchResults(anyString(), anyList()); + } + + @Test + public void testDisplayResults_ShouldRunSmartRankingIfEnabled() { + when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())) + .thenReturn(true); + mAdapter.displaySearchResults(""); + verify(mSearchFeatureProvider, times(1)).rankSearchResults(anyString(), anyList()); + } + private List getDummyDbResults() { List results = new ArrayList<>(); IntentPayload payload = new IntentPayload(new Intent());