Remove search2 package and put everything into search
Change-Id: Ie8651c79ef023f154cd7120a88a20f3481abce2b Fixes: 38502743 Test: make RunSettingsRoboTests
This commit is contained in:
377
src/com/android/settings/search/SearchFragment.java
Normal file
377
src/com/android/settings/search/SearchFragment.java
Normal file
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.search;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.LoaderManager;
|
||||
import android.content.Context;
|
||||
import android.content.Loader;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
import android.widget.SearchView;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* This fragment manages the lifecycle of indexing and searching.
|
||||
*
|
||||
* In onCreate, the indexing process is initiated in DatabaseIndexingManager.
|
||||
* While the indexing is happening, loaders are blocked from accessing the database, but the user
|
||||
* is free to start typing their query.
|
||||
*
|
||||
* When the indexing is complete, the fragment gets a callback to initialize the loaders and search
|
||||
* the query if the user has entered text.
|
||||
*/
|
||||
public class SearchFragment extends InstrumentedFragment implements SearchView.OnQueryTextListener,
|
||||
LoaderManager.LoaderCallbacks<List<? extends SearchResult>>, IndexingCallback {
|
||||
private static final String TAG = "SearchFragment";
|
||||
|
||||
@VisibleForTesting
|
||||
static final int SEARCH_TAG = "SearchViewTag".hashCode();
|
||||
|
||||
// State values
|
||||
private static final String STATE_QUERY = "state_query";
|
||||
private static final String STATE_SHOWING_SAVED_QUERY = "state_showing_saved_query";
|
||||
private static final String STATE_NEVER_ENTERED_QUERY = "state_never_entered_query";
|
||||
private static final String STATE_RESULT_CLICK_COUNT = "state_result_click_count";
|
||||
|
||||
// Loader IDs
|
||||
@VisibleForTesting
|
||||
static final int LOADER_ID_DATABASE = 1;
|
||||
@VisibleForTesting
|
||||
static final int LOADER_ID_INSTALLED_APPS = 2;
|
||||
|
||||
private static final int NUM_QUERY_LOADERS = 2;
|
||||
|
||||
@VisibleForTesting
|
||||
AtomicInteger mUnfinishedLoadersCount = new AtomicInteger(NUM_QUERY_LOADERS);
|
||||
|
||||
// Logging
|
||||
@VisibleForTesting
|
||||
static final String RESULT_CLICK_COUNT = "settings_search_result_click_count";
|
||||
|
||||
@VisibleForTesting
|
||||
String mQuery;
|
||||
|
||||
private boolean mNeverEnteredQuery = true;
|
||||
@VisibleForTesting
|
||||
boolean mShowingSavedQuery;
|
||||
private int mResultClickCount;
|
||||
private MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
@VisibleForTesting
|
||||
SavedQueryController mSavedQueryController;
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
SearchFeatureProvider mSearchFeatureProvider;
|
||||
|
||||
private SearchResultsAdapter mSearchAdapter;
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
RecyclerView mResultsRecyclerView;
|
||||
@VisibleForTesting
|
||||
SearchView mSearchView;
|
||||
private LinearLayout mNoResultsView;
|
||||
|
||||
@VisibleForTesting
|
||||
final RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
if (dy != 0) {
|
||||
hideKeyboard();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsProto.MetricsEvent.DASHBOARD_SEARCH_RESULTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
mSearchFeatureProvider = FeatureFactory.getFactory(context).getSearchFeatureProvider();
|
||||
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
mSearchAdapter = new SearchResultsAdapter(this, mSearchFeatureProvider);
|
||||
mSavedQueryController = new SavedQueryController(
|
||||
getContext(), loaderManager, mSearchAdapter);
|
||||
mSearchFeatureProvider.initFeedbackButton();
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mQuery = savedInstanceState.getString(STATE_QUERY);
|
||||
mNeverEnteredQuery = savedInstanceState.getBoolean(STATE_NEVER_ENTERED_QUERY);
|
||||
mResultClickCount = savedInstanceState.getInt(STATE_RESULT_CLICK_COUNT);
|
||||
mShowingSavedQuery = savedInstanceState.getBoolean(STATE_SHOWING_SAVED_QUERY);
|
||||
} else {
|
||||
mShowingSavedQuery = true;
|
||||
}
|
||||
|
||||
final Activity activity = getActivity();
|
||||
final ActionBar actionBar = activity.getActionBar();
|
||||
mSearchView = makeSearchView(actionBar, mQuery);
|
||||
actionBar.setCustomView(mSearchView);
|
||||
actionBar.setDisplayShowCustomEnabled(true);
|
||||
actionBar.setDisplayShowTitleEnabled(false);
|
||||
mSearchView.requestFocus();
|
||||
|
||||
// Run the Index update only if we have some space
|
||||
if (!Utils.isLowStorage(activity)) {
|
||||
mSearchFeatureProvider.updateIndex(activity, this /* indexingCallback */);
|
||||
} else {
|
||||
Log.w(TAG, "Cannot update the Indexer as we are running low on storage space!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
final View view = inflater.inflate(R.layout.search_panel, container, false);
|
||||
mResultsRecyclerView = view.findViewById(R.id.list_results);
|
||||
mResultsRecyclerView.setAdapter(mSearchAdapter);
|
||||
mResultsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
mResultsRecyclerView.addOnScrollListener(mScrollListener);
|
||||
|
||||
mNoResultsView = view.findViewById(R.id.no_results_layout);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
requery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
final Activity activity = getActivity();
|
||||
if (activity != null && activity.isFinishing()) {
|
||||
mMetricsFeatureProvider.histogram(activity, RESULT_CLICK_COUNT, mResultClickCount);
|
||||
if (mNeverEnteredQuery) {
|
||||
mMetricsFeatureProvider.action(activity,
|
||||
MetricsProto.MetricsEvent.ACTION_LEAVE_SEARCH_RESULT_WITHOUT_QUERY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString(STATE_QUERY, mQuery);
|
||||
outState.putBoolean(STATE_NEVER_ENTERED_QUERY, mNeverEnteredQuery);
|
||||
outState.putBoolean(STATE_SHOWING_SAVED_QUERY, mShowingSavedQuery);
|
||||
outState.putInt(STATE_RESULT_CLICK_COUNT, mResultClickCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String query) {
|
||||
if (TextUtils.equals(query, mQuery)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final boolean isEmptyQuery = TextUtils.isEmpty(query);
|
||||
|
||||
// Hide no-results-view when the new query is not a super-string of the previous
|
||||
if ((mQuery != null) && (mNoResultsView.getVisibility() == View.VISIBLE)
|
||||
&& (query.length() < mQuery.length())) {
|
||||
mNoResultsView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mResultClickCount = 0;
|
||||
mNeverEnteredQuery = false;
|
||||
mQuery = query;
|
||||
|
||||
// If indexing is not finished, register the query text, but don't search.
|
||||
if (!mSearchFeatureProvider.isIndexingComplete(getActivity())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isEmptyQuery) {
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
loaderManager.destroyLoader(LOADER_ID_DATABASE);
|
||||
loaderManager.destroyLoader(LOADER_ID_INSTALLED_APPS);
|
||||
mShowingSavedQuery = true;
|
||||
mSavedQueryController.loadSavedQueries();
|
||||
mSearchFeatureProvider.hideFeedbackButton();
|
||||
} else {
|
||||
restartLoaders();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
// Save submitted query.
|
||||
mSavedQueryController.saveQuery(mQuery);
|
||||
hideKeyboard();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<List<? extends SearchResult>> onCreateLoader(int id, Bundle args) {
|
||||
final Activity activity = getActivity();
|
||||
|
||||
switch (id) {
|
||||
case LOADER_ID_DATABASE:
|
||||
return mSearchFeatureProvider.getDatabaseSearchLoader(activity, mQuery);
|
||||
case LOADER_ID_INSTALLED_APPS:
|
||||
return mSearchFeatureProvider.getInstalledAppSearchLoader(activity, mQuery);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
|
||||
List<? extends SearchResult> data) {
|
||||
mSearchAdapter.addSearchResults(data, loader.getClass().getName());
|
||||
if (mUnfinishedLoadersCount.decrementAndGet() != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int resultCount = mSearchAdapter.displaySearchResults(mQuery);
|
||||
|
||||
if (resultCount == 0) {
|
||||
mNoResultsView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mNoResultsView.setVisibility(View.GONE);
|
||||
mResultsRecyclerView.scrollToPosition(0);
|
||||
}
|
||||
mSearchFeatureProvider.showFeedbackButton(this, getView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<? extends SearchResult>> loader) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called when Indexing is completed.
|
||||
*/
|
||||
@Override
|
||||
public void onIndexingFinished() {
|
||||
if (getActivity() == null) {
|
||||
return;
|
||||
}
|
||||
if (mShowingSavedQuery) {
|
||||
mSavedQueryController.loadSavedQueries();
|
||||
} else {
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
loaderManager.initLoader(LOADER_ID_DATABASE, null, this);
|
||||
loaderManager.initLoader(LOADER_ID_INSTALLED_APPS, null, this);
|
||||
}
|
||||
|
||||
requery();
|
||||
}
|
||||
|
||||
public void onSearchResultClicked() {
|
||||
mSavedQueryController.saveQuery(mQuery);
|
||||
mResultClickCount++;
|
||||
}
|
||||
|
||||
public void onSavedQueryClicked(CharSequence query) {
|
||||
final String queryString = query.toString();
|
||||
mMetricsFeatureProvider.action(getContext(),
|
||||
MetricsProto.MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_SAVED_QUERY);
|
||||
mSearchView.setQuery(queryString, false /* submit */);
|
||||
onQueryTextChange(queryString);
|
||||
}
|
||||
|
||||
public void onRemoveSavedQueryClicked(CharSequence title) {
|
||||
mSavedQueryController.removeQuery(title.toString());
|
||||
}
|
||||
|
||||
private void restartLoaders() {
|
||||
mShowingSavedQuery = false;
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
mUnfinishedLoadersCount.set(NUM_QUERY_LOADERS);
|
||||
loaderManager.restartLoader(LOADER_ID_DATABASE, null /* args */, this /* callback */);
|
||||
loaderManager.restartLoader(LOADER_ID_INSTALLED_APPS, null /* args */, this /* callback */);
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return mQuery;
|
||||
}
|
||||
|
||||
public List<SearchResult> getSearchResults() {
|
||||
return mSearchAdapter.getSearchResults();
|
||||
}
|
||||
|
||||
private void requery() {
|
||||
if (TextUtils.isEmpty(mQuery)) {
|
||||
return;
|
||||
}
|
||||
final String query = mQuery;
|
||||
mQuery = "";
|
||||
onQueryTextChange(query);
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
SearchView makeSearchView(ActionBar actionBar, String query) {
|
||||
final SearchView searchView = new SearchView(actionBar.getThemedContext());
|
||||
searchView.setIconifiedByDefault(false);
|
||||
searchView.setQuery(query, false /* submitQuery */);
|
||||
searchView.setOnQueryTextListener(this);
|
||||
searchView.setTag(SEARCH_TAG, searchView);
|
||||
final LayoutParams lp =
|
||||
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
searchView.setLayoutParams(lp);
|
||||
return searchView;
|
||||
}
|
||||
|
||||
private void hideKeyboard() {
|
||||
final Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
View view = activity.getCurrentFocus();
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
activity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
if (mResultsRecyclerView != null) {
|
||||
mResultsRecyclerView.requestFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user