Showing recents in search

A list of recent queries will display when search first launches, or
when text query is empty.

Fix: 32452066
Test: make RunSettingsRoboTests
Change-Id: Ic697f8c0795f2315a01406537c7ce1bbf9a813be
This commit is contained in:
Fan Zhang
2017-01-19 16:17:16 -08:00
parent e44be9f729
commit a8b089db8a
20 changed files with 658 additions and 38 deletions

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingStart="@dimen/preference_no_icon_padding_start"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<TextView
android:id="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItem"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"/>
<ImageView
android:id="@android:id/icon"
android:layout_width="@dimen/search_suggestion_item_image_size"
android:layout_height="@dimen/search_suggestion_item_image_size"
android:layout_marginStart="@dimen/search_suggestion_item_image_margin_start"
android:layout_marginEnd="@dimen/search_suggestion_item_image_margin_end"
android:scaleType="centerInside"
android:src="@drawable/ic_search_history"/>
</LinearLayout>

View File

@@ -266,7 +266,7 @@ public class Index {
StringBuilder sb = new StringBuilder();
sb.append("SELECT ");
sb.append(IndexDatabaseHelper.SavedQueriesColums.QUERY);
sb.append(IndexDatabaseHelper.SavedQueriesColumns.QUERY);
sb.append(" FROM ");
sb.append(Tables.TABLE_SAVED_QUERIES);
@@ -274,7 +274,7 @@ public class Index {
sb.append(" ORDER BY rowId DESC");
} else {
sb.append(" WHERE ");
sb.append(IndexDatabaseHelper.SavedQueriesColums.QUERY);
sb.append(IndexDatabaseHelper.SavedQueriesColumns.QUERY);
sb.append(" LIKE ");
sb.append("'");
sb.append(query);
@@ -1299,8 +1299,8 @@ public class Index {
final long now = new Date().getTime();
final ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.SavedQueriesColums.QUERY, params[0]);
values.put(IndexDatabaseHelper.SavedQueriesColums.TIME_STAMP, now);
values.put(IndexDatabaseHelper.SavedQueriesColumns.QUERY, params[0]);
values.put(IndexDatabaseHelper.SavedQueriesColumns.TIME_STAMP, now);
final SQLiteDatabase database = getWritableDatabase();
if (database == null) {
@@ -1312,7 +1312,7 @@ public class Index {
try {
// First, delete all saved queries that are the same
database.delete(Tables.TABLE_SAVED_QUERIES,
IndexDatabaseHelper.SavedQueriesColums.QUERY + " = ?",
IndexDatabaseHelper.SavedQueriesColumns.QUERY + " = ?",
new String[] { params[0] });
// Second, insert the saved query

View File

@@ -67,7 +67,7 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
String BUILD = "build";
}
public interface SavedQueriesColums {
public interface SavedQueriesColumns {
String QUERY = "query";
String TIME_STAMP = "timestamp";
}
@@ -127,9 +127,9 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
private static final String CREATE_SAVED_QUERIES_TABLE =
"CREATE TABLE " + Tables.TABLE_SAVED_QUERIES +
"(" +
SavedQueriesColums.QUERY + " VARCHAR(64) NOT NULL" +
SavedQueriesColumns.QUERY + " VARCHAR(64) NOT NULL" +
", " +
SavedQueriesColums.TIME_STAMP + " INTEGER" +
SavedQueriesColumns.TIME_STAMP + " INTEGER" +
")";
private static final String INSERT_BUILD_VERSION =

View File

@@ -29,7 +29,7 @@ import java.lang.annotation.RetentionPolicy;
public abstract class ResultPayload implements Parcelable {
@IntDef({PayloadType.INLINE_SLIDER, PayloadType.INLINE_SWITCH,
PayloadType.INTENT})
PayloadType.INTENT, PayloadType.SAVED_QUERY})
@Retention(RetentionPolicy.SOURCE)
public @interface PayloadType {
/**
@@ -46,6 +46,11 @@ public abstract class ResultPayload implements Parcelable {
* Result is a inline widget, using a toggle widget as UI.
*/
int INLINE_SWITCH = 2;
/**
* Result is a recently saved query.
*/
int SAVED_QUERY = 3;
}
@IntDef({SettingsSource.UNKNOWN, SettingsSource.SYSTEM, SettingsSource.SECURE,
@@ -59,5 +64,6 @@ public abstract class ResultPayload implements Parcelable {
}
@ResultPayload.PayloadType public abstract int getType();
@ResultPayload.PayloadType
public abstract int getType();
}

View File

@@ -0,0 +1,77 @@
/*
* 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.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.VisibleForTesting;
import com.android.settings.search.IndexDatabaseHelper;
import com.android.settings.search.IndexDatabaseHelper.SavedQueriesColumns;
import com.android.settings.utils.AsyncLoader;
import java.util.ArrayList;
import java.util.List;
/**
* Loader for recently searched queries.
*/
public class SavedQueryLoader extends AsyncLoader<List<SearchResult>> {
// Max number of proposed suggestions
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static final int MAX_PROPOSED_SUGGESTIONS = 5;
private final SQLiteDatabase mDatabase;
public SavedQueryLoader(Context context) {
super(context);
mDatabase = IndexDatabaseHelper.getInstance(context).getReadableDatabase();
}
@Override
protected void onDiscardResult(List<SearchResult> result) {
}
@Override
public List<SearchResult> loadInBackground() {
Cursor cursor = mDatabase.query(IndexDatabaseHelper.Tables.TABLE_SAVED_QUERIES /* table */,
new String[]{SavedQueriesColumns.QUERY} /* columns */,
null /* selection */,
null /* selectionArgs */,
null /* groupBy */,
null /* having */,
"rowId DESC" /* orderBy */,
String.valueOf(MAX_PROPOSED_SUGGESTIONS) /* limit */);
return convertCursorToResult(cursor);
}
private List<SearchResult> convertCursorToResult(Cursor cursor) {
final List<SearchResult> results = new ArrayList<>();
while (cursor.moveToNext()) {
final SavedQueryPayload payload = new SavedQueryPayload(
cursor.getString(cursor.getColumnIndex(SavedQueriesColumns.QUERY)));
results.add(new SearchResult.Builder()
.addTitle(payload.query)
.addPayload(payload)
.build());
}
return results;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.os.Parcel;
import android.support.annotation.VisibleForTesting;
/**
* {@link ResultPayload} for saved query.
*/
public class SavedQueryPayload extends ResultPayload {
public final String query;
public SavedQueryPayload(String query) {
this.query = query;
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
SavedQueryPayload(Parcel in) {
query = in.readString();
}
@Override
public int getType() {
return PayloadType.SAVED_QUERY;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(query);
}
public static final Creator<SavedQueryPayload> CREATOR = new Creator<SavedQueryPayload>() {
@Override
public SavedQueryPayload createFromParcel(Parcel in) {
return new SavedQueryPayload(in);
}
@Override
public SavedQueryPayload[] newArray(int size) {
return new SavedQueryPayload[size];
}
};
}

View File

@@ -0,0 +1,97 @@
/*
* 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.ContentValues;
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;
/**
* A background task to update saved queries.
*/
public class SavedQueryRecorder extends AsyncLoader<Void> {
private static final String LOG_TAG = "SavedQueryRecorder";
// Max number of saved search queries (who will be used for proposing suggestions)
private static long MAX_SAVED_SEARCH_QUERY = 64;
private final String mQuery;
public SavedQueryRecorder(Context context, String query) {
super(context);
mQuery = query;
}
@Override
protected void onDiscardResult(Void result) {
}
@Override
public Void loadInBackground() {
final long now = System.currentTimeMillis();
final ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.SavedQueriesColumns.QUERY, mQuery);
values.put(IndexDatabaseHelper.SavedQueriesColumns.TIME_STAMP, now);
final SQLiteDatabase database = getWritableDatabase();
if (database == null) {
return null;
}
long lastInsertedRowId;
try {
// First, delete all saved queries that are the same
database.delete(TABLE_SAVED_QUERIES,
IndexDatabaseHelper.SavedQueriesColumns.QUERY + " = ?",
new String[]{mQuery});
// Second, insert the saved query
lastInsertedRowId = database.insertOrThrow(TABLE_SAVED_QUERIES, null, values);
// Last, remove "old" saved queries
final long delta = lastInsertedRowId - MAX_SAVED_SEARCH_QUERY;
if (delta > 0) {
int count = database.delete(TABLE_SAVED_QUERIES,
"rowId <= ?",
new String[]{Long.toString(delta)});
Log.d(LOG_TAG, "Deleted '" + count + "' saved Search query(ies)");
}
} catch (Exception e) {
Log.d(LOG_TAG, "Cannot update saved Search queries", e);
}
return null;
}
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

@@ -0,0 +1,38 @@
/*
* 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.view.View;
import android.widget.TextView;
public class SavedQueryViewHolder extends SearchViewHolder {
public final TextView titleView;
public SavedQueryViewHolder(View view) {
super(view);
titleView = (TextView) view.findViewById(android.R.id.title);
}
@Override
public void onBind(SearchFragment fragment, SearchResult result) {
titleView.setText(result.title);
itemView.setOnClickListener(v -> {
fragment.onSavedQueryClicked(result.title);
});
}
}

View File

@@ -47,6 +47,11 @@ public interface SearchFeatureProvider {
*/
InstalledAppResultLoader getInstalledAppSearchLoader(Context context, String query);
/**
* Returns a new loader to get all recently saved queries search terms.
*/
SavedQueryLoader getSavedQueryLoader(Context context);
/**
* Returns the manager for indexing Settings data.
*/

View File

@@ -72,6 +72,11 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
context, new PackageManagerWrapperImpl(context.getPackageManager()), query);
}
@Override
public SavedQueryLoader getSavedQueryLoader(Context context) {
return new SavedQueryLoader(context);
}
@Override
public DatabaseIndexingManager getIndexingManager(Context context) {
if (mDatabaseIndexingManager == null) {

View File

@@ -52,8 +52,9 @@ public class SearchFragment extends InstrumentedFragment implements
private static final String STATE_RESULT_CLICK_COUNT = "state_result_click_count";
// Loader IDs
private static final int LOADER_ID_DATABASE = 0;
private static final int LOADER_ID_INSTALLED_APPS = 1;
private static final int LOADER_ID_RECENTS = 0;
private static final int LOADER_ID_DATABASE = 1;
private static final int LOADER_ID_INSTALLED_APPS = 2;
// Logging
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@@ -61,6 +62,10 @@ public class SearchFragment extends InstrumentedFragment implements
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
String mQuery;
private final SaveQueryRecorderCallback mSaveQueryRecorderCallback =
new SaveQueryRecorderCallback();
private boolean mNeverEnteredQuery = true;
private int mResultClickCount;
private MetricsFeatureProvider mMetricsFeatureProvider;
@@ -68,6 +73,7 @@ public class SearchFragment extends InstrumentedFragment implements
private SearchResultsAdapter mSearchAdapter;
private RecyclerView mResultsRecyclerView;
private SearchView mSearchView;
@Override
public int getMetricsCategory() {
@@ -86,18 +92,21 @@ public class SearchFragment extends InstrumentedFragment implements
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mSearchAdapter = new SearchResultsAdapter(this);
final LoaderManager loaderManager = getLoaderManager();
if (savedInstanceState != null) {
mQuery = savedInstanceState.getString(STATE_QUERY);
mNeverEnteredQuery = savedInstanceState.getBoolean(STATE_NEVER_ENTERED_QUERY);
mResultClickCount = savedInstanceState.getInt(STATE_RESULT_CLICK_COUNT);
final LoaderManager loaderManager = getLoaderManager();
loaderManager.initLoader(LOADER_ID_DATABASE, null, this);
loaderManager.initLoader(LOADER_ID_INSTALLED_APPS, null, this);
} else {
loaderManager.initLoader(LOADER_ID_RECENTS, null, this);
}
final Activity activity = getActivity();
final ActionBar actionBar = activity.getActionBar();
actionBar.setCustomView(makeSearchView(actionBar, mQuery));
mSearchView = makeSearchView(actionBar, mQuery);
actionBar.setCustomView(mSearchView);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
@@ -151,7 +160,10 @@ public class SearchFragment extends InstrumentedFragment implements
mSearchAdapter.clearResults();
if (TextUtils.isEmpty(mQuery)) {
getLoaderManager().destroyLoader(LOADER_ID_DATABASE);
final LoaderManager loaderManager = getLoaderManager();
loaderManager.destroyLoader(LOADER_ID_DATABASE);
loaderManager.destroyLoader(LOADER_ID_INSTALLED_APPS);
loaderManager.restartLoader(LOADER_ID_RECENTS, null /* args */, this /* callback */);
} else {
restartLoaders();
}
@@ -161,6 +173,10 @@ public class SearchFragment extends InstrumentedFragment implements
@Override
public boolean onQueryTextSubmit(String query) {
// Save submitted query.
getLoaderManager().restartLoader(SaveQueryRecorderCallback.LOADER_ID_SAVE_QUERY_TASK, null,
mSaveQueryRecorderCallback);
return true;
}
@@ -173,6 +189,8 @@ public class SearchFragment extends InstrumentedFragment implements
return mSearchFeatureProvider.getDatabaseSearchLoader(activity, mQuery);
case LOADER_ID_INSTALLED_APPS:
return mSearchFeatureProvider.getInstalledAppSearchLoader(activity, mQuery);
case LOADER_ID_RECENTS:
return mSearchFeatureProvider.getSavedQueryLoader(activity);
default:
return null;
}
@@ -191,6 +209,12 @@ public class SearchFragment extends InstrumentedFragment implements
mResultClickCount++;
}
public void onSavedQueryClicked(CharSequence query) {
final String queryString = query.toString();
mSearchView.setQuery(queryString, false /* submit */);
onQueryTextChange(queryString);
}
private void restartLoaders() {
final LoaderManager loaderManager = getLoaderManager();
loaderManager.restartLoader(LOADER_ID_DATABASE, null /* args */, this /* callback */);
@@ -207,4 +231,25 @@ public class SearchFragment extends InstrumentedFragment implements
searchView.setLayoutParams(lp);
return searchView;
}
private class SaveQueryRecorderCallback implements LoaderManager.LoaderCallbacks<Void> {
// 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;
@Override
public Loader<Void> onCreateLoader(int id, Bundle args) {
return new SavedQueryRecorder(getActivity(), mQuery);
}
@Override
public void onLoadFinished(Loader<Void> loader, Void data) {
}
@Override
public void onLoaderReset(Loader<Void> loader) {
}
}
}

View File

@@ -49,13 +49,16 @@ public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
final Context context = parent.getContext();
final LayoutInflater inflater = LayoutInflater.from(context);
final View view;
switch(viewType) {
switch (viewType) {
case PayloadType.INTENT:
view = inflater.inflate(R.layout.search_intent_item, parent, false);
return new IntentSearchViewHolder(view);
case PayloadType.INLINE_SWITCH:
view = inflater.inflate(R.layout.search_inline_switch_item, parent, false);
return new InlineSwitchViewHolder(view, context);
case PayloadType.SAVED_QUERY:
view = inflater.inflate(R.layout.search_saved_query_item, parent, false);
return new SavedQueryViewHolder(view);
default:
return null;
}

View File

@@ -21,10 +21,12 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.provider.SearchIndexableResource;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.DatabaseIndexingManager;
import com.android.settings.testutils.DatabaseTestUtils;
import org.junit.After;
import org.junit.Before;
@@ -33,7 +35,6 @@ import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -85,15 +86,7 @@ public class DatabaseIndexingManagerTest {
@After
public void cleanUp() {
Field instance;
Class clazz = IndexDatabaseHelper.class;
try {
instance = clazz.getDeclaredField("sSingleton");
instance.setAccessible(true);
instance.set(null, null);
} catch (Exception e) {
throw new RuntimeException();
}
DatabaseTestUtils.clearDb();
}
@Test

View File

@@ -20,10 +20,13 @@ package com.android.settings.search;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.DatabaseIndexingUtils;
import com.android.settings.search2.DatabaseResultLoader;
import com.android.settings.testutils.DatabaseTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -31,8 +34,6 @@ import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.lang.reflect.Field;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@@ -53,15 +54,7 @@ public class DatabaseResultLoaderTest {
@After
public void cleanUp() {
Field instance;
Class clazz = IndexDatabaseHelper.class;
try {
instance = clazz.getDeclaredField("sSingleton");
instance.setAccessible(true);
instance.set(null, null);
} catch (Exception e) {
throw new RuntimeException();
}
DatabaseTestUtils.clearDb();
}
@Test

View File

@@ -0,0 +1,79 @@
/*
* 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.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search.IndexDatabaseHelper;
import com.android.settings.testutils.DatabaseTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.List;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SavedQueryLoaderTest {
private Context mContext;
private SQLiteDatabase mDb;
private SavedQueryLoader mLoader;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
mLoader = new SavedQueryLoader(mContext);
setUpDb();
}
@After
public void cleanUp() {
DatabaseTestUtils.clearDb();
}
@Test
public void loadInBackground_shouldReturnSavedQueries() {
final List<SearchResult> results = mLoader.loadInBackground();
assertThat(results.size()).isEqualTo(SavedQueryLoader.MAX_PROPOSED_SUGGESTIONS);
for (SearchResult result : results) {
assertThat(result.viewType).isEqualTo(ResultPayload.PayloadType.SAVED_QUERY);
}
}
private void setUpDb() {
final long now = System.currentTimeMillis();
for (int i = 0; i < SavedQueryLoader.MAX_PROPOSED_SUGGESTIONS + 2; i++) {
ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.SavedQueriesColumns.QUERY, String.valueOf(i));
values.put(IndexDatabaseHelper.SavedQueriesColumns.TIME_STAMP, now);
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_SAVED_QUERIES, null, values);
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SavedQueryPayloadTest {
private SavedQueryPayload mPayload;
@Test
public void getType_shouldBeSavedQueryType() {
mPayload = new SavedQueryPayload("Test");
assertThat(mPayload.getType()).isEqualTo(ResultPayload.PayloadType.SAVED_QUERY);
}
}

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 com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.testutils.DatabaseTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.List;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SavedQueryRecorderTest {
private Context mContext;
private SavedQueryRecorder mRecorder;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
}
@After
public void cleanUp() {
DatabaseTestUtils.clearDb();
}
@Test
public void canSaveQueryToDb() {
final String query = "test";
mRecorder = new SavedQueryRecorder(mContext, query);
mRecorder.loadInBackground();
final SavedQueryLoader loader = new SavedQueryLoader(mContext);
List<SearchResult> results = loader.loadInBackground();
assertThat(results.size()).isEqualTo(1);
assertThat(results.get(0).title).isEqualTo(query);
}
}

View File

@@ -39,6 +39,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -52,6 +53,9 @@ public class SearchFragmentTest {
private DatabaseResultLoader mDatabaseResultLoader;
@Mock
private InstalledAppResultLoader mInstalledAppResultLoader;
@Mock
private SavedQueryLoader mSavedQueryLoader;
private FakeFeatureFactory mFeatureFactory;
@Before
@@ -65,6 +69,8 @@ public class SearchFragmentTest {
when(mFeatureFactory.searchFeatureProvider
.getInstalledAppSearchLoader(any(Context.class), anyString()))
.thenReturn(mInstalledAppResultLoader);
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
}
@Test
@@ -114,6 +120,27 @@ public class SearchFragmentTest {
.getInstalledAppSearchLoader(any(Context.class), anyString());
}
@Test
public void queryTextChangeToEmpty_shouldTriggerSavedQueryLoader() {
ActivityController<SearchActivity> activityController =
Robolectric.buildActivity(SearchActivity.class);
activityController.setup();
SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
.findFragmentById(R.id.main_content);
fragment.onQueryTextChange("");
activityController.get().onBackPressed();
activityController.pause().stop().destroy();
verify(mFeatureFactory.searchFeatureProvider, never())
.getDatabaseSearchLoader(any(Context.class), anyString());
verify(mFeatureFactory.searchFeatureProvider, never())
.getInstalledAppSearchLoader(any(Context.class), anyString());
// Saved query loaded 2 times: fragment start, and query change to empty.
verify(mFeatureFactory.searchFeatureProvider, times(2))
.getSavedQueryLoader(any(Context.class));
}
@Test
public void updateIndex_TriggerOnCreate() {
ActivityController<SearchActivity> activityController =

View File

@@ -0,0 +1,36 @@
/*
* 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.testutils;
import com.android.settings.search.IndexDatabaseHelper;
import java.lang.reflect.Field;
public class DatabaseTestUtils {
public static void clearDb() {
Field instance;
Class clazz = IndexDatabaseHelper.class;
try {
instance = clazz.getDeclaredField("sSingleton");
instance.setAccessible(true);
instance.set(null, null);
} catch (Exception e) {
throw new RuntimeException();
}
}
}