Add search loader for installed apps.
- The loader filters out system apps. - Loader performs case-insensitive match with app names. - SearchResultAdapter combines results from multiple loaders into a single list. Fixes: 33347966 Test: make RunSettingsRoboTests Change-Id: I228ca6fb82f0ac5151b2346c079c2de41104a4df
This commit is contained in:
@@ -18,6 +18,7 @@ package com.android.settings.applications;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
|
||||
import java.util.List;
|
||||
@@ -29,24 +30,30 @@ import java.util.List;
|
||||
* the API version supported by Robolectric.
|
||||
*/
|
||||
public interface PackageManagerWrapper {
|
||||
|
||||
/**
|
||||
* Returns the real {@code PackageManager} object.
|
||||
*/
|
||||
PackageManager getPackageManager();
|
||||
|
||||
/**
|
||||
* Calls {@code PackageManager.getInstalledApplicationsAsUser()}.
|
||||
*
|
||||
* @see android.content.pm.PackageManager.PackageManager#getInstalledApplicationsAsUser
|
||||
* @see android.content.pm.PackageManager#getInstalledApplicationsAsUser
|
||||
*/
|
||||
List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId);
|
||||
|
||||
/**
|
||||
* Calls {@code PackageManager.hasSystemFeature()}.
|
||||
*
|
||||
* @see android.content.pm.PackageManager.PackageManager#hasSystemFeature
|
||||
* @see android.content.pm.PackageManager#hasSystemFeature
|
||||
*/
|
||||
boolean hasSystemFeature(String name);
|
||||
|
||||
/**
|
||||
* Calls {@code PackageManager.queryIntentActivitiesAsUser()}.
|
||||
*
|
||||
* @see android.content.pm.PackageManager.PackageManager#queryIntentActivitiesAsUser
|
||||
* @see android.content.pm.PackageManager#queryIntentActivitiesAsUser
|
||||
*/
|
||||
List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId);
|
||||
}
|
||||
|
@@ -24,12 +24,18 @@ import android.content.pm.ResolveInfo;
|
||||
import java.util.List;
|
||||
|
||||
public class PackageManagerWrapperImpl implements PackageManagerWrapper {
|
||||
|
||||
private final PackageManager mPm;
|
||||
|
||||
public PackageManagerWrapperImpl(PackageManager pm) {
|
||||
mPm = pm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageManager getPackageManager() {
|
||||
return mPm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
|
||||
return mPm.getInstalledApplicationsAsUser(flags, userId);
|
||||
|
@@ -23,10 +23,11 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.search.Index;
|
||||
import com.android.settings.search.IndexDatabaseHelper;
|
||||
import com.android.settings.utils.AsyncLoader;
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -107,7 +108,6 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
|
||||
icon = mContext.getDrawable(R.drawable.ic_search_history);
|
||||
}
|
||||
|
||||
|
||||
SearchResult.Builder builder = new SearchResult.Builder();
|
||||
builder.addTitle(title)
|
||||
.addSummary(summaryOn)
|
||||
|
139
src/com/android/settings/search2/InstalledAppResultLoader.java
Normal file
139
src/com/android/settings/search2/InstalledAppResultLoader.java
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.settings.search2;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.settings.applications.PackageManagerWrapper;
|
||||
import com.android.settings.utils.AsyncLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Search loader for installed apps.
|
||||
*/
|
||||
public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> {
|
||||
|
||||
private static final int NAME_NO_MATCH = -1;
|
||||
private static final int NAME_EXACT_MATCH = 0;
|
||||
|
||||
private final String mQuery;
|
||||
private final UserManager mUserManager;
|
||||
private final PackageManagerWrapper mPackageManager;
|
||||
|
||||
public InstalledAppResultLoader(Context context, PackageManagerWrapper pmWrapper,
|
||||
String query) {
|
||||
super(context);
|
||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
mPackageManager = pmWrapper;
|
||||
mQuery = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchResult> loadInBackground() {
|
||||
final List<SearchResult> results = new ArrayList<>();
|
||||
final PackageManager pm = mPackageManager.getPackageManager();
|
||||
|
||||
for (UserInfo user : getUsersToCount()) {
|
||||
final List<ApplicationInfo> apps =
|
||||
mPackageManager.getInstalledApplicationsAsUser(
|
||||
PackageManager.MATCH_DISABLED_COMPONENTS
|
||||
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
|
||||
| (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
|
||||
user.id);
|
||||
for (ApplicationInfo info : apps) {
|
||||
if (info.isSystemApp()) {
|
||||
continue;
|
||||
}
|
||||
final CharSequence label = info.loadLabel(pm);
|
||||
final int wordDiff = getWordDifference(label.toString(), mQuery);
|
||||
if (wordDiff == NAME_NO_MATCH) {
|
||||
continue;
|
||||
}
|
||||
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
.setData(Uri.fromParts("package", info.packageName, null));
|
||||
|
||||
final SearchResult.Builder builder = new SearchResult.Builder();
|
||||
builder.addIcon(info.loadIcon(pm))
|
||||
.addTitle(info.loadLabel(pm))
|
||||
.addRank(wordDiff)
|
||||
.addPayload(new IntentPayload(intent));
|
||||
results.add(builder.build());
|
||||
}
|
||||
}
|
||||
Collections.sort(results);
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDiscardResult(List<SearchResult> result) {
|
||||
|
||||
}
|
||||
|
||||
private List<UserInfo> getUsersToCount() {
|
||||
return mUserManager.getProfiles(UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "difference" between appName and query string. appName must contain all
|
||||
* characters from query, in the same order. If not, returns NAME_NO_MATCH. If they do match,
|
||||
* returns an int value representing how different they are, NAME_EXACT_MATCH means they match
|
||||
* perfectly, and larger values means they are less similar.
|
||||
* <p/>
|
||||
* Example:
|
||||
* appName: Abcde, query: Abcde, Returns NAME_EXACT_MATCH
|
||||
* appName: Abcde, query: ade, Returns 2
|
||||
* appName: Abcde, query: ae, Returns 3
|
||||
* appName: Abcde, query: ea, Returns NAME_NO_MATCH
|
||||
* appName: Abcde, query: xyz, Returns NAME_NO_MATCH
|
||||
*/
|
||||
private int getWordDifference(String appName, String query) {
|
||||
if (TextUtils.isEmpty(appName) || TextUtils.isEmpty(query)) {
|
||||
return NAME_NO_MATCH;
|
||||
}
|
||||
final char[] queryTokens = query.toString().toLowerCase().toCharArray();
|
||||
final char[] valueText = appName.toLowerCase().toCharArray();
|
||||
if (queryTokens.length > valueText.length) {
|
||||
return NAME_NO_MATCH;
|
||||
}
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
while (i < valueText.length && j < queryTokens.length) {
|
||||
if (valueText[i++] == queryTokens[j]) {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (j != queryTokens.length) {
|
||||
return NAME_NO_MATCH;
|
||||
}
|
||||
// Use the diff in length as a proxy of how close the 2 words match. Value range from 0
|
||||
// to infinity.
|
||||
return valueText.length - queryTokens.length;
|
||||
}
|
||||
}
|
@@ -15,9 +15,11 @@
|
||||
*/
|
||||
package com.android.settings.search2;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
@@ -25,6 +27,7 @@ import com.android.settings.R;
|
||||
* The DatabaseResultLoader is the primary use case for this ViewHolder.
|
||||
*/
|
||||
public class IntentSearchViewHolder extends SearchViewHolder {
|
||||
|
||||
public final TextView titleView;
|
||||
public final TextView summaryView;
|
||||
public final ImageView iconView;
|
||||
@@ -33,12 +36,19 @@ public class IntentSearchViewHolder extends SearchViewHolder {
|
||||
super(view);
|
||||
titleView = (TextView) view.findViewById(R.id.title);
|
||||
summaryView = (TextView) view.findViewById(R.id.summary);
|
||||
iconView= (ImageView) view.findViewById(R.id.icon);
|
||||
iconView = (ImageView) view.findViewById(R.id.icon);
|
||||
}
|
||||
|
||||
public void onBind(SearchResult result) {
|
||||
@Override
|
||||
public void onBind(Fragment fragment, SearchResult result) {
|
||||
titleView.setText(result.title);
|
||||
summaryView.setText(result.summary);
|
||||
iconView.setImageDrawable(result.icon);
|
||||
itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
fragment.startActivity(((IntentPayload) result.payload).intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -41,4 +41,9 @@ public interface SearchFeatureProvider {
|
||||
* Returns a new loader to search in index database.
|
||||
*/
|
||||
DatabaseResultLoader getDatabaseSearchLoader(Context context, String query);
|
||||
|
||||
/**
|
||||
* Returns a new loader to search installed apps.
|
||||
*/
|
||||
InstalledAppResultLoader getInstalledAppSearchLoader(Context context, String query);
|
||||
}
|
||||
|
@@ -19,14 +19,11 @@ package com.android.settings.search2;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.SearchView;
|
||||
import android.view.Menu;
|
||||
|
||||
import android.view.MenuItem;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.utils.AsyncLoader;
|
||||
|
||||
import java.util.List;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.PackageManagerWrapperImpl;
|
||||
|
||||
/**
|
||||
* FeatureProvider for the refactored search code.
|
||||
@@ -51,15 +48,15 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
||||
}
|
||||
String menuTitle = mContext.getString(R.string.search_menu);
|
||||
MenuItem menuItem = menu.add(Menu.NONE, Menu.NONE, Menu.NONE, menuTitle)
|
||||
.setIcon(R.drawable.abc_ic_search_api_material)
|
||||
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
Intent intent = new Intent(activity, SearchActivity.class);
|
||||
activity.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
.setIcon(R.drawable.abc_ic_search_api_material)
|
||||
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
Intent intent = new Intent(activity, SearchActivity.class);
|
||||
activity.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
}
|
||||
@@ -68,4 +65,10 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
||||
public DatabaseResultLoader getDatabaseSearchLoader(Context context, String query) {
|
||||
return new DatabaseResultLoader(context, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstalledAppResultLoader getInstalledAppSearchLoader(Context context, String query) {
|
||||
return new InstalledAppResultLoader(
|
||||
context, new PackageManagerWrapperImpl(context.getPackageManager()), query);
|
||||
}
|
||||
}
|
||||
|
@@ -46,13 +46,13 @@ public class SearchFragment extends InstrumentedFragment implements
|
||||
static final String STATE_QUERY = "query";
|
||||
|
||||
// Loader IDs
|
||||
private static final int DATABASE_LOADER_ID = 0;
|
||||
private static final int LOADER_ID_DATABASE = 0;
|
||||
private static final int LOADER_ID_INSTALLED_APPS = 1;
|
||||
|
||||
@VisibleForTesting
|
||||
String mQuery;
|
||||
|
||||
private SearchFeatureProvider mSearchFeatureProvider;
|
||||
private DatabaseResultLoader mSearchLoader;
|
||||
|
||||
private SearchResultsAdapter mSearchAdapter;
|
||||
private RecyclerView mResultsRecyclerView;
|
||||
@@ -73,10 +73,12 @@ public class SearchFragment extends InstrumentedFragment implements
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
mSearchAdapter = new SearchResultsAdapter();
|
||||
mSearchAdapter = new SearchResultsAdapter(this);
|
||||
if (savedInstanceState != null) {
|
||||
mQuery = savedInstanceState.getString(STATE_QUERY);
|
||||
getLoaderManager().initLoader(DATABASE_LOADER_ID, null, this);
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
loaderManager.initLoader(LOADER_ID_DATABASE, null, this);
|
||||
loaderManager.initLoader(LOADER_ID_INSTALLED_APPS, null, this);
|
||||
}
|
||||
final ActionBar actionBar = getActivity().getActionBar();
|
||||
actionBar.setCustomView(makeSearchView(actionBar, mQuery));
|
||||
@@ -109,7 +111,7 @@ public class SearchFragment extends InstrumentedFragment implements
|
||||
mSearchAdapter.clearResults();
|
||||
|
||||
if (TextUtils.isEmpty(mQuery)) {
|
||||
getLoaderManager().destroyLoader(DATABASE_LOADER_ID);
|
||||
getLoaderManager().destroyLoader(LOADER_ID_DATABASE);
|
||||
} else {
|
||||
restartLoaders();
|
||||
}
|
||||
@@ -127,9 +129,10 @@ public class SearchFragment extends InstrumentedFragment implements
|
||||
final Activity activity = getActivity();
|
||||
|
||||
switch (id) {
|
||||
case DATABASE_LOADER_ID:
|
||||
mSearchLoader = mSearchFeatureProvider.getDatabaseSearchLoader(activity, mQuery);
|
||||
return mSearchLoader;
|
||||
case LOADER_ID_DATABASE:
|
||||
return mSearchFeatureProvider.getDatabaseSearchLoader(activity, mQuery);
|
||||
case LOADER_ID_INSTALLED_APPS:
|
||||
return mSearchFeatureProvider.getInstalledAppSearchLoader(activity, mQuery);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -137,10 +140,6 @@ public class SearchFragment extends InstrumentedFragment implements
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<SearchResult>> loader, List<SearchResult> data) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mSearchAdapter.mergeResults(data, loader.getClass().getName());
|
||||
}
|
||||
|
||||
@@ -150,7 +149,8 @@ public class SearchFragment extends InstrumentedFragment implements
|
||||
|
||||
private void restartLoaders() {
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
loaderManager.restartLoader(DATABASE_LOADER_ID, null /* args */, this /* callback */);
|
||||
loaderManager.restartLoader(LOADER_ID_DATABASE, null /* args */, this /* callback */);
|
||||
loaderManager.restartLoader(LOADER_ID_INSTALLED_APPS, null /* args */, this /* callback */);
|
||||
}
|
||||
|
||||
private SearchView makeSearchView(ActionBar actionBar, String query) {
|
||||
|
@@ -19,11 +19,73 @@ package com.android.settings.search2;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Dataclass as an interface for all Search Results.
|
||||
* Data class as an interface for all Search Results.
|
||||
*/
|
||||
public class SearchResult implements Comparable<SearchResult> {
|
||||
|
||||
/**
|
||||
* The title of the result and main text displayed.
|
||||
* Intent Results: Displays as the primary
|
||||
*/
|
||||
public final CharSequence title;
|
||||
|
||||
/**
|
||||
* Summary / subtitle text
|
||||
* Intent Results: Displays the text underneath the title
|
||||
*/
|
||||
final public CharSequence summary;
|
||||
|
||||
/**
|
||||
* An ordered list of the information hierarchy.
|
||||
* Intent Results: Displayed a hierarchy of selections to reach the setting from the home screen
|
||||
*/
|
||||
public final ArrayList<String> breadcrumbs;
|
||||
|
||||
/**
|
||||
* A suggestion for the ranking of the result.
|
||||
* Based on Settings Rank:
|
||||
* 1 is a near perfect match
|
||||
* 9 is the weakest match
|
||||
* TODO subject to change
|
||||
*/
|
||||
public final int rank;
|
||||
|
||||
/**
|
||||
* Identifier for the recycler view adapter.
|
||||
*/
|
||||
@ResultPayload.PayloadType
|
||||
public final int viewType;
|
||||
|
||||
/**
|
||||
* Metadata for the specific result types.
|
||||
*/
|
||||
public final ResultPayload payload;
|
||||
|
||||
/**
|
||||
* Result's icon.
|
||||
*/
|
||||
public final Drawable icon;
|
||||
|
||||
/**
|
||||
* Stable id for this object.
|
||||
*/
|
||||
public final long stableId;
|
||||
|
||||
private SearchResult(Builder builder) {
|
||||
title = builder.mTitle;
|
||||
summary = builder.mSummary;
|
||||
breadcrumbs = builder.mBreadcrumbs;
|
||||
rank = builder.mRank;
|
||||
icon = builder.mIcon;
|
||||
payload = builder.mResultPayload;
|
||||
viewType = payload.getType();
|
||||
stableId = Objects.hash(title, summary, breadcrumbs, rank, icon, payload, viewType);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(SearchResult searchResult) {
|
||||
if (searchResult == null) {
|
||||
@@ -33,19 +95,19 @@ public class SearchResult implements Comparable<SearchResult> {
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
protected String mTitle;
|
||||
protected String mSummary;
|
||||
protected CharSequence mTitle;
|
||||
protected CharSequence mSummary;
|
||||
protected ArrayList<String> mBreadcrumbs;
|
||||
protected int mRank = -1;
|
||||
protected ResultPayload mResultPayload;
|
||||
protected Drawable mIcon;
|
||||
|
||||
public Builder addTitle(String title) {
|
||||
public Builder addTitle(CharSequence title) {
|
||||
mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addSummary(String summary) {
|
||||
public Builder addSummary(CharSequence summary) {
|
||||
mSummary = summary;
|
||||
return this;
|
||||
}
|
||||
@@ -77,10 +139,6 @@ public class SearchResult implements Comparable<SearchResult> {
|
||||
// Check that all of the mandatory fields are set.
|
||||
if (mTitle == null) {
|
||||
throw new IllegalArgumentException("SearchResult missing title argument");
|
||||
} else if (mSummary == null ) {
|
||||
throw new IllegalArgumentException("SearchResult missing summary argument");
|
||||
} else if (mBreadcrumbs == null){
|
||||
throw new IllegalArgumentException("SearchResult missing breadcrumbs argument");
|
||||
} else if (mRank == -1) {
|
||||
throw new IllegalArgumentException("SearchResult missing rank argument");
|
||||
} else if (mIcon == null) {
|
||||
@@ -91,56 +149,4 @@ public class SearchResult implements Comparable<SearchResult> {
|
||||
return new SearchResult(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The title of the result and main text displayed.
|
||||
* Intent Results: Displays as the primary
|
||||
*/
|
||||
public final String title;
|
||||
|
||||
/**
|
||||
* Summary / subtitle text
|
||||
* Intent Results: Displays the text underneath the title
|
||||
*/
|
||||
final public String summary;
|
||||
|
||||
/**
|
||||
* An ordered list of the information hierarchy.
|
||||
* Intent Results: Displayed a hierarchy of selections to reach the setting from the home screen
|
||||
*/
|
||||
public final ArrayList<String> breadcrumbs;
|
||||
|
||||
/**
|
||||
* A suggestion for the ranking of the result.
|
||||
* Based on Settings Rank:
|
||||
* 1 is a near perfect match
|
||||
* 9 is the weakest match
|
||||
* TODO subject to change
|
||||
*/
|
||||
public final int rank;
|
||||
|
||||
/**
|
||||
* Identifier for the recycler view adapter.
|
||||
*/
|
||||
@ResultPayload.PayloadType public final int viewType;
|
||||
|
||||
/**
|
||||
* Metadata for the specific result types.
|
||||
*/
|
||||
public final ResultPayload payload;
|
||||
|
||||
/**
|
||||
* Result's icon.
|
||||
*/
|
||||
public final Drawable icon;
|
||||
|
||||
private SearchResult(Builder builder) {
|
||||
title = builder.mTitle;
|
||||
summary = builder.mSummary;
|
||||
breadcrumbs = builder.mBreadcrumbs;
|
||||
rank = builder.mRank;
|
||||
icon = builder.mIcon;
|
||||
payload = builder.mResultPayload;
|
||||
viewType = payload.getType();
|
||||
}
|
||||
}
|
||||
|
@@ -33,40 +33,19 @@ import java.util.Map;
|
||||
public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
|
||||
private final List<SearchResult> mSearchResults;
|
||||
private final Map<String, List<SearchResult>> mResultsMap;
|
||||
private final SearchFragment mFragment;
|
||||
|
||||
public SearchResultsAdapter() {
|
||||
public SearchResultsAdapter(SearchFragment fragment) {
|
||||
mFragment = fragment;
|
||||
mSearchResults = new ArrayList<>();
|
||||
mResultsMap = new HashMap<>();
|
||||
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
public void mergeResults(List<SearchResult> freshResults, String loaderClassName) {
|
||||
if (freshResults == null) {
|
||||
return;
|
||||
}
|
||||
mResultsMap.put(loaderClassName, freshResults);
|
||||
mSearchResults.addAll(mergeMappedResults());
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void clearResults() {
|
||||
mSearchResults.clear();
|
||||
mResultsMap.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private ArrayList<SearchResult> mergeMappedResults() {
|
||||
ArrayList<SearchResult> mergedResults = new ArrayList<>();
|
||||
for (String key : mResultsMap.keySet()) {
|
||||
mergedResults.addAll(mResultsMap.get(key));
|
||||
}
|
||||
return mergedResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
switch (viewType) {
|
||||
case PayloadType.INTENT:
|
||||
View view = inflater.inflate(R.layout.search_intent_item, parent, false);
|
||||
@@ -82,13 +61,12 @@ public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(SearchViewHolder holder, int position) {
|
||||
SearchResult result = mSearchResults.get(position);
|
||||
holder.onBind(result);
|
||||
holder.onBind(mFragment, mSearchResults.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return super.getItemId(position);
|
||||
return mSearchResults.get(position).stableId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,6 +79,23 @@ public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
|
||||
return mSearchResults.size();
|
||||
}
|
||||
|
||||
public void mergeResults(List<SearchResult> freshResults, String loaderClassName) {
|
||||
if (freshResults == null) {
|
||||
return;
|
||||
}
|
||||
mResultsMap.put(loaderClassName, freshResults);
|
||||
final int oldSize = mSearchResults.size();
|
||||
mSearchResults.addAll(freshResults);
|
||||
final int newSize = mSearchResults.size();
|
||||
notifyItemRangeInserted(oldSize, newSize - oldSize);
|
||||
}
|
||||
|
||||
public void clearResults() {
|
||||
mSearchResults.clear();
|
||||
mResultsMap.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public List<SearchResult> getSearchResults() {
|
||||
return mSearchResults;
|
||||
|
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.settings.search2;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
@@ -29,5 +30,5 @@ public abstract class SearchViewHolder extends RecyclerView.ViewHolder {
|
||||
super(view);
|
||||
}
|
||||
|
||||
public abstract void onBind(SearchResult result);
|
||||
public abstract void onBind(Fragment fragment, SearchResult result);
|
||||
}
|
Reference in New Issue
Block a user