Merge "Fork Search code to independantly develop and test search."

This commit is contained in:
TreeHugger Robot
2016-11-18 00:11:50 +00:00
committed by Android (Google) Code Review
29 changed files with 1861 additions and 8 deletions

View File

@@ -136,6 +136,8 @@ import com.android.settings.print.PrintSettingsFragment;
import com.android.settings.qstile.DevelopmentTiles;
import com.android.settings.search.DynamicIndexableContentMonitor;
import com.android.settings.search.Index;
import com.android.settings.search2.SearchFeatureProvider;
import com.android.settings.search2.SearchFragment;
import com.android.settings.sim.SimSettings;
import com.android.settings.system.SystemDashboardFragment;
import com.android.settings.tts.TextToSpeechSettings;
@@ -477,6 +479,8 @@ public class SettingsActivity extends SettingsDrawerActivity
private SearchResultsSummary mSearchResultsFragment;
private String mSearchQuery;
private SearchFeatureProvider mSearchFeatureProvider;
// Categories
private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
@@ -526,9 +530,14 @@ public class SettingsActivity extends SettingsDrawerActivity
}
MenuInflater inflater = getMenuInflater();
if (mSearchFeatureProvider.isEnabled()) {
mSearchFeatureProvider.setUpSearchMenu(menu, this);
return true;
}
inflater.inflate(R.menu.options_menu, menu);
// Cache the search query (can be overriden by the OnQueryTextListener)
// Cache the search query (can be overridden by the OnQueryTextListener)
final String query = mSearchQuery;
mSearchMenuItem = menu.findItem(R.id.search);
@@ -551,7 +560,6 @@ public class SettingsActivity extends SettingsDrawerActivity
mSearchMenuItem.expandActionView();
}
mSearchView.setQuery(query, true /* submit */);
return true;
}
@@ -594,8 +602,12 @@ public class SettingsActivity extends SettingsDrawerActivity
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
long startTime = System.currentTimeMillis();
mDashboardFeatureProvider =
FeatureFactory.getFactory(this).getDashboardFeatureProvider(this);
final FeatureFactory factory = FeatureFactory.getFactory(this);
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);
mSearchFeatureProvider = factory.getSearchFeatureProvider(this);
// Should happen before any call to getIntent()
getMetaData();
@@ -1279,19 +1291,24 @@ public class SettingsActivity extends SettingsDrawerActivity
return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
}
@Deprecated
@Override
public boolean onQueryTextSubmit(String query) {
switchToSearchResultsFragmentIfNeeded();
if (mSearchFeatureProvider.isEnabled()) {
return false;
}
mSearchQuery = query;
switchToSearchResultsFragmentIfNeeded();
return mSearchResultsFragment.onQueryTextSubmit(query);
}
@Deprecated
@Override
public boolean onQueryTextChange(String newText) {
mSearchQuery = newText;
if (mSearchResultsFragment == null) {
if (mSearchFeatureProvider.isEnabled() || mSearchResultsFragment == null) {
return false;
}
mSearchQuery = newText;
return mSearchResultsFragment.onQueryTextChange(newText);
}
@@ -1335,6 +1352,7 @@ public class SettingsActivity extends SettingsDrawerActivity
}
}
@Deprecated
private void switchToSearchResultsFragmentIfNeeded() {
if (mSearchResultsFragment != null) {
return;
@@ -1352,10 +1370,12 @@ public class SettingsActivity extends SettingsDrawerActivity
mSearchMenuItemExpanded = true;
}
@Deprecated
public void needToRevertToInitialFragment() {
mNeedToRevertToInitialFragment = true;
}
@Deprecated
private void revertToInitialFragment() {
mNeedToRevertToInitialFragment = false;
mSearchResultsFragment = null;

View File

@@ -27,6 +27,7 @@ import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProvider;
import com.android.settings.search2.SearchFeatureProvider;
/**
* Abstract class for creating feature controllers. Allows OEM implementations to define their own
@@ -80,6 +81,8 @@ public abstract class FeatureFactory {
public abstract EnterprisePrivacyFeatureProvider getEnterprisePrivacyFeatureProvider(
Context context);
public abstract SearchFeatureProvider getSearchFeatureProvider(Context context);
public static final class FactoryNotFoundException extends RuntimeException {
public FactoryNotFoundException(Throwable throwable) {
super("Unable to create factory. Did you misconfigure Proguard?", throwable);

View File

@@ -32,6 +32,8 @@ import com.android.settings.enterprise.EnterprisePrivacyFeatureProviderImpl;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProviderImpl;
import com.android.settings.search2.SearchFeatureProvider;
import com.android.settings.search2.SearchFeatureProviderImpl;
/**
* {@link FeatureFactory} implementation for AOSP Settings.
@@ -44,6 +46,7 @@ public final class FeatureFactoryImpl extends FeatureFactory {
private DashboardFeatureProviderImpl mDashboardFeatureProvider;
private LocaleFeatureProvider mLocaleFeatureProvider;
private EnterprisePrivacyFeatureProvider mEnterprisePrivacyFeatureProvider;
private SearchFeatureProvider mSearchFeatureProvider;
@Override
public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -91,9 +94,17 @@ public final class FeatureFactoryImpl extends FeatureFactory {
public EnterprisePrivacyFeatureProvider getEnterprisePrivacyFeatureProvider(Context context) {
if (mEnterprisePrivacyFeatureProvider == null) {
mEnterprisePrivacyFeatureProvider = new EnterprisePrivacyFeatureProviderImpl(context,
new DevicePolicyManagerWrapperImpl((DevicePolicyManager)context
new DevicePolicyManagerWrapperImpl((DevicePolicyManager) context
.getSystemService(Context.DEVICE_POLICY_SERVICE)));
}
return mEnterprisePrivacyFeatureProvider;
}
@Override
public SearchFeatureProvider getSearchFeatureProvider(Context context) {
if (mSearchFeatureProvider == null) {
mSearchFeatureProvider = new SearchFeatureProviderImpl(context);
}
return mSearchFeatureProvider;
}
}

View File

@@ -0,0 +1,124 @@
/*
* 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.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.Drawable;
import android.support.annotation.VisibleForTesting;
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;
import java.util.List;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK;
/**
* AsyncTask to retrieve Settings, First party app and any intent based results.
*/
public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
private final String mQueryText;
private final Context mContext;
protected final SQLiteDatabase mDatabase;
public DatabaseResultLoader(Context context, String queryText) {
super(context);
mDatabase = IndexDatabaseHelper.getInstance(context).getReadableDatabase();
mQueryText = queryText;
mContext = context;
}
@Override
protected void onDiscardResult(List<SearchResult> result) {
// TODO Search
}
@Override
public List<SearchResult> loadInBackground() {
if (mQueryText == null || mQueryText.isEmpty()) {
return null;
}
String query = getSQLQuery();
Cursor result = mDatabase.rawQuery(query, null);
return parseCursorForSearch(result);
}
@Override
protected boolean onCancelLoad() {
// TODO
return super.onCancelLoad();
}
protected String getSQLQuery() {
return String.format("SELECT data_rank, data_title, data_summary_on, " +
"data_summary_off, data_entries, data_keywords, class_name, screen_title, icon, " +
"intent_action, intent_target_package, intent_target_class, enabled, " +
"data_key_reference FROM prefs_index WHERE prefs_index MATCH 'data_title:%s* " +
"OR data_title_normalized:%s* OR data_keywords:%s*' AND locale = 'en_US'",
mQueryText, mQueryText, mQueryText);
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public ArrayList<SearchResult> parseCursorForSearch(Cursor cursorResults) {
if (cursorResults == null) {
return null;
}
final ArrayList<SearchResult> results = new ArrayList<>();
while (cursorResults.moveToNext()) {
final String title = cursorResults.getString(Index.COLUMN_INDEX_TITLE);
final String summaryOn = cursorResults.getString(COLUMN_INDEX_RAW_SUMMARY_ON);
final ArrayList<String> breadcrumbs = new ArrayList<>();
final int rank = cursorResults.getInt(COLUMN_INDEX_XML_RES_RANK);
final String intentString = cursorResults.getString(Index.COLUMN_INDEX_INTENT_ACTION);
final IntentPayload intentPayload = new IntentPayload(new Intent(intentString));
final int iconID = cursorResults.getInt(COLUMN_INDEX_RAW_ICON_RESID);
Drawable icon;
try {
icon = mContext.getDrawable(iconID);
} catch (Resources.NotFoundException nfe) {
icon = mContext.getDrawable(R.drawable.ic_search_history);
}
SearchResult.Builder builder = new SearchResult.Builder();
builder.addTitle(title)
.addSummary(summaryOn)
.addBreadcrumbs(breadcrumbs)
.addRank(rank)
.addIcon(icon)
.addPayload(intentPayload);
results.add(builder.build());
}
Collections.sort(results);
return results;
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.net.Uri;
import android.os.Parcel;
/**
* Payload for Inline Settings results represented by a Slider.
*/
public class InlineSliderPayload extends ResultPayload {
public final Uri uri;
private InlineSliderPayload(Parcel in) {
uri = in.readParcelable(InlineSliderPayload.class.getClassLoader());
}
public InlineSliderPayload(Uri newUri) {
uri = newUri;
}
@Override
public int getType() {
return PayloadType.INLINE_SLIDER;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(uri, flags);
}
public static final Creator<InlineSliderPayload> CREATOR = new Creator<InlineSliderPayload>() {
@Override
public InlineSliderPayload createFromParcel(Parcel in) {
return new InlineSliderPayload(in);
}
@Override
public InlineSliderPayload[] newArray(int size) {
return new InlineSliderPayload[size];
}
};
}

View File

@@ -0,0 +1,63 @@
/*
* 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.Intent;
import android.os.Parcel;
import com.android.internal.annotations.VisibleForTesting;
/**
* Encapsulates the standard intent based results as seen in first party apps and Settings results.
*/
public class IntentPayload extends ResultPayload {
public final Intent intent;
private IntentPayload(Parcel in) {
intent = in.readParcelable(IntentPayload.class.getClassLoader());
}
public IntentPayload(Intent newIntent) {
intent = newIntent;
}
@ResultPayload.PayloadType public int getType() {
return PayloadType.INTENT;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(intent, flags);
}
public static final Creator<IntentPayload> CREATOR = new Creator<IntentPayload>() {
@Override
public IntentPayload createFromParcel(Parcel in) {
return new IntentPayload(in);
}
@Override
public IntentPayload[] newArray(int size) {
return new IntentPayload[size];
}
};
}

View File

@@ -0,0 +1,44 @@
/*
* 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.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.settings.R;
/**
* ViewHolder for intent based search results.
* 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;
public IntentSearchViewHolder(View view) {
super(view);
titleView = (TextView) view.findViewById(R.id.title);
summaryView = (TextView) view.findViewById(R.id.summary);
iconView= (ImageView) view.findViewById(R.id.icon);
}
public void onBind(SearchResult result) {
titleView.setText(result.title);
summaryView.setText(result.summary);
iconView.setImageDrawable(result.icon);
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.annotation.IntDef;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* A interface for search results types. Examples include Inline results, third party apps
* or any future possibilities.
*/
public abstract class ResultPayload implements Parcelable {
@IntDef({PayloadType.INLINE_SLIDER, PayloadType.INLINE_SWITCH, PayloadType.INTENT})
@Retention(RetentionPolicy.SOURCE)
public @interface PayloadType {
int INTENT = 0;
int INLINE_SLIDER = 1;
int INLINE_SWITCH = 2;
}
@ResultPayload.PayloadType public abstract int getType();
}

View File

@@ -0,0 +1,41 @@
/*
* 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.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import com.android.settings.R;
public class SearchActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_main);
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.main_content);
if (fragment == null) {
fragmentManager.beginTransaction()
.add(R.id.main_content, new SearchFragment())
.commit();
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.app.Activity;
import android.widget.SearchView;
import android.view.Menu;
/**
* FeatureProvider for Settings Search
*/
public interface SearchFeatureProvider {
/**
* @return true to use the new version of search
*/
boolean isEnabled();
/**
* Inserts the Menu items into Settings activity.
* @param menu Items will be inserted into this menu.
* @param activity The activity that precedes SearchActivity.
*/
void setUpSearchMenu(Menu menu, Activity activity);
}

View File

@@ -0,0 +1,63 @@
/*
* 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.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;
/**
* FeatureProvider for the refactored search code.
*/
public class SearchFeatureProviderImpl implements SearchFeatureProvider {
protected Context mContext;
public SearchFeatureProviderImpl(Context context) {
mContext = context;
}
@Override
public boolean isEnabled() {
return false;
}
@Override
public void setUpSearchMenu(Menu menu, final Activity activity) {
if (menu == null || activity == null) {
return;
}
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;
}
});
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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.app.Activity;
import android.content.Loader;
import android.os.Bundle;
import android.app.LoaderManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Menu;
import android.view.MenuInflater;
import android.widget.SearchView;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment;
import java.util.List;
public class SearchFragment extends InstrumentedFragment implements
SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener,
LoaderManager.LoaderCallbacks<List<SearchResult>> {
private static final int DATABASE_LOADER_ID = 0;
private SearchResultsAdapter mSearchAdapter;
private DatabaseResultLoader mSearchLoader;
private RecyclerView mResultsRecyclerView;
private SearchView mSearchView;
private MenuItem mSearchMenuItem;
private String mQuery;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mSearchAdapter = new SearchResultsAdapter();
final LoaderManager loaderManager = getLoaderManager();
loaderManager.initLoader(DATABASE_LOADER_ID, null, this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.search_panel_2, container, false);
mResultsRecyclerView = (RecyclerView) view.findViewById(R.id.list_results);
mResultsRecyclerView.setAdapter(mSearchAdapter);
mResultsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
return view;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.search_options_menu, menu);
mSearchMenuItem = menu.findItem(R.id.search);
mSearchView = (SearchView) mSearchMenuItem.getActionView();
mSearchView.setOnQueryTextListener(this);
mSearchView.setMaxWidth(Integer.MAX_VALUE);
mSearchMenuItem.expandActionView();
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// Return false to prevent the search box from collapsing.
return false;
}
@Override
public boolean onQueryTextChange(String query) {
if (query == null || query.equals(mQuery)) {
return false;
}
mQuery = query;
clearLoaders();
final LoaderManager loaderManager = getLoaderManager();
loaderManager.restartLoader(DATABASE_LOADER_ID, null, this);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public Loader<List<SearchResult>> onCreateLoader(int id, Bundle args) {
final Activity activity = getActivity();
switch (id) {
case DATABASE_LOADER_ID:
mSearchLoader = new DatabaseResultLoader(activity, mQuery);
return mSearchLoader;
default:
return null;
}
}
@Override
public void onLoadFinished(Loader<List<SearchResult>> loader, List<SearchResult> data) {
if (data == null) {
return;
}
mSearchAdapter.mergeResults(data, loader.getClass().getName());
}
@Override
public void onLoaderReset(Loader<List<SearchResult>> loader) { }
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.DASHBOARD_SEARCH_RESULTS;
}
private void clearLoaders() {
if (mSearchLoader != null) {
mSearchLoader.cancelLoad();
mSearchLoader = null;
}
}
}

View File

@@ -0,0 +1,146 @@
/*
* 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.graphics.drawable.Drawable;
import java.util.ArrayList;
/**
* Dataclass as an interface for all Search Results.
*/
public class SearchResult implements Comparable<SearchResult> {
@Override
public int compareTo(SearchResult searchResult) {
if (searchResult == null) {
return -1;
}
return this.rank - searchResult.rank;
}
public static class Builder {
protected String mTitle;
protected String mSummary;
protected ArrayList<String> mBreadcrumbs;
protected int mRank = -1;
protected ResultPayload mResultPayload;
protected Drawable mIcon;
public Builder addTitle(String title) {
mTitle = title;
return this;
}
public Builder addSummary(String summary) {
mSummary = summary;
return this;
}
public Builder addBreadcrumbs(ArrayList<String> breadcrumbs) {
mBreadcrumbs = breadcrumbs;
return this;
}
public Builder addRank(int rank) {
if (rank < 0 || rank > 9) {
rank = 42;
}
mRank = rank;
return this;
}
public Builder addIcon(Drawable icon) {
mIcon = icon;
return this;
}
public Builder addPayload(ResultPayload payload) {
mResultPayload = payload;
return this;
}
public SearchResult build() {
// 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) {
throw new IllegalArgumentException("SearchResult missing icon argument");
} else if (mResultPayload == null) {
throw new IllegalArgumentException("SearchResult missing Payload argument");
}
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();
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.support.annotation.VisibleForTesting;
import android.support.v7.widget.RecyclerView.Adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.settings.R;
import com.android.settings.search2.ResultPayload.PayloadType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
private ArrayList<SearchResult> mSearchResults;
private HashMap<String, List<SearchResult>> mResultsMap;
public SearchResultsAdapter() {
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 = mergeMappedResults();
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());
switch(viewType) {
case PayloadType.INTENT:
View view = inflater.inflate(R.layout.search_intent_item, parent, false);
return new IntentSearchViewHolder(view);
case PayloadType.INLINE_SLIDER:
return null;
case PayloadType.INLINE_SWITCH:
return null;
default:
return null;
}
}
@Override
public void onBindViewHolder(SearchViewHolder holder, int position) {
SearchResult result = mSearchResults.get(position);
holder.onBind(result);
}
@Override
public long getItemId(int position) {
return super.getItemId(position);
}
@Override
public int getItemViewType(int position) {
return mSearchResults.get(position).viewType;
}
@Override
public int getItemCount() {
return mSearchResults.size();
}
@VisibleForTesting
public ArrayList<SearchResult> getSearchResults() {
return mSearchResults;
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.support.v7.widget.RecyclerView;
import android.view.View;
/**
* The ViewHolder for the Search RecyclerView.
* There are multiple search result types in the same Recycler view with different UI requirements.
* Some examples include Intent results, Inline results, and Help articles.
*/
public abstract class SearchViewHolder extends RecyclerView.ViewHolder {
public SearchViewHolder(View view) {
super(view);
}
public abstract void onBind(SearchResult result);
}