Merge "Add button to remove saved search query"

This commit is contained in:
TreeHugger Robot
2017-03-15 19:42:41 +00:00
committed by Android (Google) Code Review
8 changed files with 135 additions and 29 deletions

View File

@@ -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>

View File

@@ -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>

View 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;
}
}
}

View File

@@ -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));
});
} }
} }

View File

@@ -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

View File

@@ -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();
} }
} }

View File

@@ -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));
} }
} }

View File

@@ -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));