Adding APIs for smart search ranking.
Test: RunSettingsRoboTests Bug: 36866337 Change-Id: I20a26e5ac7a57582cea24951120717a4f2c93ce0
This commit is contained in:
@@ -51,6 +51,7 @@ public class IntentSearchViewHolder extends SearchViewHolder {
|
|||||||
mMetricsFeatureProvider.action(v.getContext(),
|
mMetricsFeatureProvider.action(v.getContext(),
|
||||||
MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_RESULT,
|
MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_RESULT,
|
||||||
resultName, rank);
|
resultName, rank);
|
||||||
|
mSearchFeatureProvider.searchResultClicked(fragment.mQuery, result);
|
||||||
fragment.startActivity(intent);
|
fragment.startActivity(intent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,8 @@ import android.view.Menu;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import com.android.settings.dashboard.SiteMapManager;
|
import com.android.settings.dashboard.SiteMapManager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FeatureProvider for Settings Search
|
* FeatureProvider for Settings Search
|
||||||
*/
|
*/
|
||||||
@@ -89,5 +91,29 @@ public interface SearchFeatureProvider {
|
|||||||
default void hideFeedbackButton() {
|
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<SearchResult> 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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -117,7 +117,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
final LoaderManager loaderManager = getLoaderManager();
|
final LoaderManager loaderManager = getLoaderManager();
|
||||||
mSearchAdapter = new SearchResultsAdapter(this);
|
mSearchAdapter = new SearchResultsAdapter(this, mSearchFeatureProvider);
|
||||||
mSavedQueryController = new SavedQueryController(
|
mSavedQueryController = new SavedQueryController(
|
||||||
getContext(), loaderManager, mSearchAdapter);
|
getContext(), loaderManager, mSearchAdapter);
|
||||||
mSearchFeatureProvider.initFeedbackButton();
|
mSearchFeatureProvider.initFeedbackButton();
|
||||||
@@ -251,7 +251,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
|||||||
if (mUnfinishedLoadersCount.decrementAndGet() != 0) {
|
if (mUnfinishedLoadersCount.decrementAndGet() != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final int resultCount = mSearchAdapter.displaySearchResults();
|
final int resultCount = mSearchAdapter.displaySearchResults(mQuery);
|
||||||
mNoResultsView.setVisibility(resultCount == 0 ? View.VISIBLE : View.GONE);
|
mNoResultsView.setVisibility(resultCount == 0 ? View.VISIBLE : View.GONE);
|
||||||
mSearchFeatureProvider.showFeedbackButton(this, getView());
|
mSearchFeatureProvider.showFeedbackButton(this, getView());
|
||||||
}
|
}
|
||||||
|
@@ -40,11 +40,14 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
|||||||
private final List<SearchResult> mSearchResults;
|
private final List<SearchResult> mSearchResults;
|
||||||
private final SearchFragment mFragment;
|
private final SearchFragment mFragment;
|
||||||
private Map<String, List<? extends SearchResult>> mResultsMap;
|
private Map<String, List<? extends SearchResult>> mResultsMap;
|
||||||
|
private final SearchFeatureProvider mSearchFeatureProvider;
|
||||||
|
|
||||||
public SearchResultsAdapter(SearchFragment fragment) {
|
public SearchResultsAdapter(SearchFragment fragment,
|
||||||
|
SearchFeatureProvider searchFeatureProvider) {
|
||||||
mFragment = fragment;
|
mFragment = fragment;
|
||||||
mSearchResults = new ArrayList<>();
|
mSearchResults = new ArrayList<>();
|
||||||
mResultsMap = new ArrayMap<>();
|
mResultsMap = new ArrayMap<>();
|
||||||
|
mSearchFeatureProvider = searchFeatureProvider;
|
||||||
|
|
||||||
setHasStableIds(true);
|
setHasStableIds(true);
|
||||||
}
|
}
|
||||||
@@ -119,9 +122,10 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
|||||||
* Merge the results from each of the loaders into one list for the adapter.
|
* Merge the results from each of the loaders into one list for the adapter.
|
||||||
* Prioritizes results from the local database over installed apps.
|
* Prioritizes results from the local database over installed apps.
|
||||||
*
|
*
|
||||||
|
* @param query user query corresponding to these results
|
||||||
* @return Number of matched results
|
* @return Number of matched results
|
||||||
*/
|
*/
|
||||||
public int displaySearchResults() {
|
public int displaySearchResults(String query) {
|
||||||
final List<? extends SearchResult> databaseResults = mResultsMap
|
final List<? extends SearchResult> databaseResults = mResultsMap
|
||||||
.get(DatabaseResultLoader.class.getName());
|
.get(DatabaseResultLoader.class.getName());
|
||||||
final List<? extends SearchResult> installedAppResults = mResultsMap
|
final List<? extends SearchResult> installedAppResults = mResultsMap
|
||||||
@@ -151,6 +155,12 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
|||||||
results.add(installedAppResults.get(appIndex++));
|
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);
|
mSearchResults.addAll(results);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
|
|
||||||
|
@@ -40,11 +40,14 @@ public abstract class SearchViewHolder extends RecyclerView.ViewHolder {
|
|||||||
public final ImageView iconView;
|
public final ImageView iconView;
|
||||||
|
|
||||||
protected final MetricsFeatureProvider mMetricsFeatureProvider;
|
protected final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
|
protected final SearchFeatureProvider mSearchFeatureProvider;
|
||||||
|
|
||||||
public SearchViewHolder(View view) {
|
public SearchViewHolder(View view) {
|
||||||
super(view);
|
super(view);
|
||||||
mMetricsFeatureProvider = FeatureFactory.getFactory(view.getContext())
|
final FeatureFactory featureFactory = FeatureFactory
|
||||||
.getMetricsFeatureProvider();
|
.getFactory(view.getContext().getApplicationContext());
|
||||||
|
mMetricsFeatureProvider = featureFactory.getMetricsFeatureProvider();
|
||||||
|
mSearchFeatureProvider = featureFactory.getSearchFeatureProvider();
|
||||||
titleView = view.findViewById(android.R.id.title);
|
titleView = view.findViewById(android.R.id.title);
|
||||||
summaryView = view.findViewById(android.R.id.summary);
|
summaryView = view.findViewById(android.R.id.summary);
|
||||||
iconView = view.findViewById(android.R.id.icon);
|
iconView = view.findViewById(android.R.id.icon);
|
||||||
|
@@ -34,6 +34,7 @@ import com.android.settings.search2.InstalledAppResultLoader;
|
|||||||
import com.android.settings.search2.IntentPayload;
|
import com.android.settings.search2.IntentPayload;
|
||||||
import com.android.settings.search2.IntentSearchViewHolder;
|
import com.android.settings.search2.IntentSearchViewHolder;
|
||||||
import com.android.settings.search2.ResultPayload;
|
import com.android.settings.search2.ResultPayload;
|
||||||
|
import com.android.settings.search2.SearchFeatureProvider;
|
||||||
import com.android.settings.search2.SearchFragment;
|
import com.android.settings.search2.SearchFragment;
|
||||||
import com.android.settings.search2.SearchResult;
|
import com.android.settings.search2.SearchResult;
|
||||||
import com.android.settings.search2.SearchResult.Builder;
|
import com.android.settings.search2.SearchResult.Builder;
|
||||||
@@ -48,6 +49,14 @@ import org.mockito.MockitoAnnotations;
|
|||||||
import org.robolectric.Robolectric;
|
import org.robolectric.Robolectric;
|
||||||
import org.robolectric.annotation.Config;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -59,6 +68,10 @@ public class SearchResultsAdapterTest {
|
|||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private SearchFragment mFragment;
|
private SearchFragment mFragment;
|
||||||
|
@Mock
|
||||||
|
private SearchFeatureProvider mSearchFeatureProvider;
|
||||||
|
@Mock
|
||||||
|
private Context mMockContext;
|
||||||
private SearchResultsAdapter mAdapter;
|
private SearchResultsAdapter mAdapter;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private String mLoaderClassName;
|
private String mLoaderClassName;
|
||||||
@@ -67,8 +80,10 @@ public class SearchResultsAdapterTest {
|
|||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mContext = Robolectric.buildActivity(Activity.class).get();
|
mContext = Robolectric.buildActivity(Activity.class).get();
|
||||||
mAdapter = new SearchResultsAdapter(mFragment);
|
mAdapter = new SearchResultsAdapter(mFragment, mSearchFeatureProvider);
|
||||||
mLoaderClassName = DatabaseResultLoader.class.getName();
|
mLoaderClassName = DatabaseResultLoader.class.getName();
|
||||||
|
when(mFragment.getContext()).thenReturn(mMockContext);
|
||||||
|
when(mMockContext.getApplicationContext()).thenReturn(mContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -81,7 +96,7 @@ public class SearchResultsAdapterTest {
|
|||||||
public void testSingleSourceMerge_ExactCopyReturned() {
|
public void testSingleSourceMerge_ExactCopyReturned() {
|
||||||
ArrayList<SearchResult> intentResults = getIntentSampleResults();
|
ArrayList<SearchResult> intentResults = getIntentSampleResults();
|
||||||
mAdapter.addSearchResults(intentResults, mLoaderClassName);
|
mAdapter.addSearchResults(intentResults, mLoaderClassName);
|
||||||
mAdapter.displaySearchResults();
|
mAdapter.displaySearchResults("");
|
||||||
|
|
||||||
List<SearchResult> updatedResults = mAdapter.getSearchResults();
|
List<SearchResult> updatedResults = mAdapter.getSearchResults();
|
||||||
assertThat(updatedResults).containsAllIn(intentResults);
|
assertThat(updatedResults).containsAllIn(intentResults);
|
||||||
@@ -109,7 +124,7 @@ public class SearchResultsAdapterTest {
|
|||||||
InstalledAppResultLoader.class.getName());
|
InstalledAppResultLoader.class.getName());
|
||||||
mAdapter.addSearchResults(getDummyDbResults(),
|
mAdapter.addSearchResults(getDummyDbResults(),
|
||||||
DatabaseResultLoader.class.getName());
|
DatabaseResultLoader.class.getName());
|
||||||
int count = mAdapter.displaySearchResults();
|
int count = mAdapter.displaySearchResults("");
|
||||||
|
|
||||||
List<SearchResult> results = mAdapter.getSearchResults();
|
List<SearchResult> results = mAdapter.getSearchResults();
|
||||||
assertThat(results.get(0).title).isEqualTo("alpha");
|
assertThat(results.get(0).title).isEqualTo("alpha");
|
||||||
@@ -121,6 +136,22 @@ public class SearchResultsAdapterTest {
|
|||||||
assertThat(count).isEqualTo(6);
|
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<SearchResult> getDummyDbResults() {
|
private List<SearchResult> getDummyDbResults() {
|
||||||
List<SearchResult> results = new ArrayList<>();
|
List<SearchResult> results = new ArrayList<>();
|
||||||
IntentPayload payload = new IntentPayload(new Intent());
|
IntentPayload payload = new IntentPayload(new Intent());
|
||||||
|
Reference in New Issue
Block a user