Merge "Add button to remove saved search query"
This commit is contained in:
committed by
Android (Google) Code Review
commit
b86719765a
@@ -20,26 +20,28 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical">
|
||||||
android:paddingStart="@dimen/preference_no_icon_padding_start"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@android:id/title"
|
android:id="@android:id/title"
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
android:layout_height="match_parent"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_weight="1"/>
|
android:layout_weight="1"
|
||||||
|
android:text="test"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:paddingStart="@dimen/preference_no_icon_padding_start"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItem" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@android:id/icon"
|
android:id="@android:id/icon"
|
||||||
android:layout_width="@dimen/search_suggestion_item_image_size"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
android:layout_height="@dimen/search_suggestion_item_image_size"
|
android:layout_width="@dimen/dashboard_category_height"
|
||||||
android:layout_marginStart="@dimen/search_suggestion_item_image_margin_start"
|
android:layout_height="@dimen/dashboard_category_height"
|
||||||
android:layout_marginEnd="@dimen/search_suggestion_item_image_margin_end"
|
android:paddingStart="@dimen/search_suggestion_item_image_margin_start"
|
||||||
android:scaleType="centerInside"
|
android:paddingEnd="@dimen/search_suggestion_item_image_margin_end"
|
||||||
android:src="@drawable/ic_search_history"/>
|
android:scaleType="center"
|
||||||
|
android:src="@drawable/ic_cross_grey_24dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@@ -137,7 +137,7 @@
|
|||||||
|
|
||||||
<!-- Suggestion item image margin start / end -->
|
<!-- Suggestion item image margin start / end -->
|
||||||
<dimen name="search_suggestion_item_image_margin_start">32dp</dimen>
|
<dimen name="search_suggestion_item_image_margin_start">32dp</dimen>
|
||||||
<dimen name="search_suggestion_item_image_margin_end">16dp</dimen>
|
<dimen name="search_suggestion_item_image_margin_end">32dp</dimen>
|
||||||
|
|
||||||
<!-- Dimensions for Wifi Assistant Card -->
|
<!-- Dimensions for Wifi Assistant Card -->
|
||||||
<dimen name="wifi_assistant_padding_top_bottom">16dp</dimen>
|
<dimen name="wifi_assistant_padding_top_bottom">16dp</dimen>
|
||||||
|
67
src/com/android/settings/search2/SavedQueryRemover.java
Normal file
67
src/com/android/settings/search2/SavedQueryRemover.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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.search2;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.settings.search.IndexDatabaseHelper;
|
||||||
|
import com.android.settings.utils.AsyncLoader;
|
||||||
|
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_SAVED_QUERIES;
|
||||||
|
|
||||||
|
public class SavedQueryRemover extends AsyncLoader<Void> {
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "SavedQueryRemover";
|
||||||
|
|
||||||
|
private final String mQuery;
|
||||||
|
|
||||||
|
public SavedQueryRemover(Context context, String query) {
|
||||||
|
super(context);
|
||||||
|
mQuery = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void loadInBackground() {
|
||||||
|
final SQLiteDatabase database = getWritableDatabase();
|
||||||
|
try {
|
||||||
|
// First, delete all saved queries that are the same
|
||||||
|
database.delete(TABLE_SAVED_QUERIES,
|
||||||
|
IndexDatabaseHelper.SavedQueriesColumns.QUERY + " = ?",
|
||||||
|
new String[]{mQuery});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d(LOG_TAG, "Cannot update saved Search queries", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDiscardResult(Void result) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private SQLiteDatabase getWritableDatabase() {
|
||||||
|
try {
|
||||||
|
return IndexDatabaseHelper.getInstance(getContext()).getWritableDatabase();
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(LOG_TAG, "Cannot open writable database", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -22,17 +22,18 @@ import android.widget.TextView;
|
|||||||
public class SavedQueryViewHolder extends SearchViewHolder {
|
public class SavedQueryViewHolder extends SearchViewHolder {
|
||||||
|
|
||||||
public final TextView titleView;
|
public final TextView titleView;
|
||||||
|
public final View removeButton;
|
||||||
|
|
||||||
public SavedQueryViewHolder(View view) {
|
public SavedQueryViewHolder(View view) {
|
||||||
super(view);
|
super(view);
|
||||||
titleView = view.findViewById(android.R.id.title);
|
titleView = view.findViewById(android.R.id.title);
|
||||||
|
removeButton = view.findViewById(android.R.id.icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(SearchFragment fragment, SearchResult result) {
|
public void onBind(SearchFragment fragment, SearchResult result) {
|
||||||
titleView.setText(result.title);
|
titleView.setText(result.title);
|
||||||
itemView.setOnClickListener(v -> {
|
titleView.setOnClickListener(v -> fragment.onSavedQueryClicked(result.title));
|
||||||
fragment.onSavedQueryClicked(result.title);
|
removeButton.setOnClickListener(v -> fragment.onRemoveSavedQueryClicked(result.title));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -74,8 +74,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
String mQuery;
|
String mQuery;
|
||||||
|
|
||||||
private final SaveQueryRecorderCallback mSaveQueryRecorderCallback =
|
private final SaveQueryCallback mSaveQueryCallback =
|
||||||
new SaveQueryRecorderCallback();
|
new SaveQueryCallback();
|
||||||
|
|
||||||
private boolean mNeverEnteredQuery = true;
|
private boolean mNeverEnteredQuery = true;
|
||||||
private int mResultClickCount;
|
private int mResultClickCount;
|
||||||
@@ -218,8 +218,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
|||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextSubmit(String query) {
|
public boolean onQueryTextSubmit(String query) {
|
||||||
// Save submitted query.
|
// Save submitted query.
|
||||||
getLoaderManager().restartLoader(SaveQueryRecorderCallback.LOADER_ID_SAVE_QUERY_TASK, null,
|
getLoaderManager().restartLoader(SaveQueryCallback.LOADER_ID_SAVE_QUERY_TASK, null,
|
||||||
mSaveQueryRecorderCallback);
|
mSaveQueryCallback);
|
||||||
hideKeyboard();
|
hideKeyboard();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -264,6 +264,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onSearchResultClicked() {
|
public void onSearchResultClicked() {
|
||||||
|
getLoaderManager().restartLoader(SaveQueryCallback.LOADER_ID_SAVE_QUERY_TASK, null,
|
||||||
|
mSaveQueryCallback);
|
||||||
mResultClickCount++;
|
mResultClickCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,6 +277,13 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
|||||||
onQueryTextChange(queryString);
|
onQueryTextChange(queryString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onRemoveSavedQueryClicked(CharSequence title) {
|
||||||
|
final Bundle args = new Bundle();
|
||||||
|
args.putString(SaveQueryCallback.ARG_REMOVE_QUERY, title.toString());
|
||||||
|
getLoaderManager().restartLoader(SaveQueryCallback.LOADER_ID_REMOVE_QUERY_TASK,
|
||||||
|
args, mSaveQueryCallback);
|
||||||
|
}
|
||||||
|
|
||||||
private void restartLoaders() {
|
private void restartLoaders() {
|
||||||
final LoaderManager loaderManager = getLoaderManager();
|
final LoaderManager loaderManager = getLoaderManager();
|
||||||
mUnfinishedLoadersCount.set(NUM_QUERY_LOADERS);
|
mUnfinishedLoadersCount.set(NUM_QUERY_LOADERS);
|
||||||
@@ -317,19 +326,31 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SaveQueryRecorderCallback implements LoaderManager.LoaderCallbacks<Void> {
|
private class SaveQueryCallback implements LoaderManager.LoaderCallbacks<Void> {
|
||||||
// TODO: make a generic background task manager to handle one-off tasks like this one.
|
// TODO: make a generic background task manager to handle one-off tasks like this one.
|
||||||
|
|
||||||
private static final int LOADER_ID_SAVE_QUERY_TASK = 0;
|
private static final int LOADER_ID_SAVE_QUERY_TASK = 0;
|
||||||
|
private static final int LOADER_ID_REMOVE_QUERY_TASK = 1;
|
||||||
|
private static final String ARG_REMOVE_QUERY = "remove_query";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loader<Void> onCreateLoader(int id, Bundle args) {
|
public Loader<Void> onCreateLoader(int id, Bundle args) {
|
||||||
|
switch (id) {
|
||||||
|
case LOADER_ID_SAVE_QUERY_TASK:
|
||||||
return new SavedQueryRecorder(getActivity(), mQuery);
|
return new SavedQueryRecorder(getActivity(), mQuery);
|
||||||
|
case LOADER_ID_REMOVE_QUERY_TASK:
|
||||||
|
return new SavedQueryRemover(getActivity(), args.getString(ARG_REMOVE_QUERY));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadFinished(Loader<Void> loader, Void data) {
|
public void onLoadFinished(Loader<Void> loader, Void data) {
|
||||||
|
switch (loader.getId()) {
|
||||||
|
case LOADER_ID_REMOVE_QUERY_TASK:
|
||||||
|
getLoaderManager().restartLoader(LOADER_ID_RECENTS, null, SearchFragment.this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -36,10 +36,11 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||||
public class SavedQueryRecorderTest {
|
public class SavedQueryRecorderAndRemoverTest {
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private SavedQueryRecorder mRecorder;
|
private SavedQueryRecorder mRecorder;
|
||||||
|
private SavedQueryRemover mRemover;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
@@ -52,16 +53,25 @@ public class SavedQueryRecorderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void canSaveQueryToDb() {
|
public void canSaveAndRemoveQuery() {
|
||||||
final String query = "test";
|
final String query = "test";
|
||||||
mRecorder = new SavedQueryRecorder(mContext, query);
|
mRecorder = new SavedQueryRecorder(mContext, query);
|
||||||
|
mRemover = new SavedQueryRemover(mContext, query);
|
||||||
|
|
||||||
|
// Record a new query and load all queries from DB
|
||||||
mRecorder.loadInBackground();
|
mRecorder.loadInBackground();
|
||||||
|
|
||||||
final SavedQueryLoader loader = new SavedQueryLoader(mContext);
|
final SavedQueryLoader loader = new SavedQueryLoader(mContext);
|
||||||
List<? extends SearchResult> results = loader.loadInBackground();
|
List<? extends SearchResult> results = loader.loadInBackground();
|
||||||
|
|
||||||
|
// Should contain the newly recorded query
|
||||||
assertThat(results.size()).isEqualTo(1);
|
assertThat(results.size()).isEqualTo(1);
|
||||||
assertThat(results.get(0).title).isEqualTo(query);
|
assertThat(results.get(0).title).isEqualTo(query);
|
||||||
|
|
||||||
|
// Remove the query and load all queries from DB
|
||||||
|
mRemover.loadInBackground();
|
||||||
|
results = loader.loadInBackground();
|
||||||
|
|
||||||
|
// Saved query list should be empty because it's removed.
|
||||||
|
assertThat(results).isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -45,6 +45,8 @@ public class SavedQueryViewHolderTest {
|
|||||||
private Context mContext;
|
private Context mContext;
|
||||||
private SavedQueryViewHolder mHolder;
|
private SavedQueryViewHolder mHolder;
|
||||||
private View mView;
|
private View mView;
|
||||||
|
private View mTitleView;
|
||||||
|
private View mRemoveButton;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
@@ -52,6 +54,8 @@ public class SavedQueryViewHolderTest {
|
|||||||
mContext = RuntimeEnvironment.application;
|
mContext = RuntimeEnvironment.application;
|
||||||
mView = LayoutInflater.from(mContext)
|
mView = LayoutInflater.from(mContext)
|
||||||
.inflate(R.layout.search_saved_query_item, null);
|
.inflate(R.layout.search_saved_query_item, null);
|
||||||
|
mTitleView = mView.findViewById(android.R.id.title);
|
||||||
|
mRemoveButton = mView.findViewById(android.R.id.icon);
|
||||||
mHolder = new SavedQueryViewHolder(mView);
|
mHolder = new SavedQueryViewHolder(mView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,8 +64,10 @@ public class SavedQueryViewHolderTest {
|
|||||||
final SearchResult result = mock(SearchResult.class);
|
final SearchResult result = mock(SearchResult.class);
|
||||||
mHolder.onBind(mSearchFragment, result);
|
mHolder.onBind(mSearchFragment, result);
|
||||||
|
|
||||||
mView.performClick();
|
mTitleView.performClick();
|
||||||
|
mRemoveButton.performClick();
|
||||||
|
|
||||||
verify(mSearchFragment).onSavedQueryClicked(any(CharSequence.class));
|
verify(mSearchFragment).onSavedQueryClicked(any(CharSequence.class));
|
||||||
|
verify(mSearchFragment).onRemoveSavedQueryClicked(any(CharSequence.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -276,7 +276,6 @@ public class SearchFragmentTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onLoadFinished_ShowsFeedback() {
|
public void onLoadFinished_ShowsFeedback() {
|
||||||
|
|
||||||
when(mFeatureFactory.searchFeatureProvider
|
when(mFeatureFactory.searchFeatureProvider
|
||||||
.getDatabaseSearchLoader(any(Context.class), anyString()))
|
.getDatabaseSearchLoader(any(Context.class), anyString()))
|
||||||
.thenReturn(new MockDBLoader(RuntimeEnvironment.application));
|
.thenReturn(new MockDBLoader(RuntimeEnvironment.application));
|
||||||
|
Reference in New Issue
Block a user