Update DatabaseLoader querying.

Change the way the queries are built and sorted for the
database loader. It now constructs 3 queries that match
different columns. Each query returns their results
and gets sorted, then merged into the master list of
results.

Change-Id: Id643422ddfe26bfec89e04ee9f5832b99148e22a
Test: make RunSettingsRoboTests
Bug: 32904057, 32903623
This commit is contained in:
Matthew Fritze
2016-12-20 15:50:56 -08:00
parent d5e2bfcc7a
commit 7fda314980
7 changed files with 462 additions and 110 deletions

View File

@@ -35,10 +35,12 @@ import com.android.settings.search.Index;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_RANK; import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_ID;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_TITLE; import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_TITLE;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_SUMMARY_ON; import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_SUMMARY_ON;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_CLASS_NAME; import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_CLASS_NAME;
@@ -63,21 +65,28 @@ class CursorToSearchResultConverter {
private final String TAG = "CursorConverter"; private final String TAG = "CursorConverter";
private final String mQueryText;
private final Context mContext; private final Context mContext;
public CursorToSearchResultConverter(Context context) { private final Set<String> mKeys;
public CursorToSearchResultConverter(Context context, String queryText) {
mContext = context; mContext = context;
mKeys = new HashSet<>();
mQueryText = queryText;
} }
public List<SearchResult> convertCursor(Cursor cursorResults) { public List<SearchResult> convertCursor(Cursor cursorResults, int baseRank) {
if (cursorResults == null) { if (cursorResults == null) {
return null; return null;
} }
final Map<String, Context> contextMap = new HashMap<>(); final Map<String, Context> contextMap = new HashMap<>();
final ArrayList<SearchResult> results = new ArrayList<>(); final List<SearchResult> results = new ArrayList<>();
while (cursorResults.moveToNext()) { while (cursorResults.moveToNext()) {
SearchResult result = buildSingleSearchResultFromCursor(contextMap, cursorResults); SearchResult result = buildSingleSearchResultFromCursor(contextMap, cursorResults,
baseRank);
if (result != null) { if (result != null) {
results.add(result); results.add(result);
} }
@@ -87,13 +96,22 @@ class CursorToSearchResultConverter {
} }
private SearchResult buildSingleSearchResultFromCursor(Map<String, Context> contextMap, private SearchResult buildSingleSearchResultFromCursor(Map<String, Context> contextMap,
Cursor cursor) { Cursor cursor, int baseRank) {
final String docId = cursor.getString(COLUMN_INDEX_ID);
/* Make sure that this result has not yet been added as a result. Checking the docID
covers the case of multiple queries matching the same row, but we need to also to check
for potentially the same named or slightly varied names pointing to the same page.
*/
if (mKeys.contains(docId)) {
return null;
}
mKeys.add(docId);
final String pkgName = cursor.getString(COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE); final String pkgName = cursor.getString(COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
final String action = cursor.getString(COLUMN_INDEX_INTENT_ACTION); final String action = cursor.getString(COLUMN_INDEX_INTENT_ACTION);
final String title = cursor.getString(COLUMN_INDEX_TITLE); final String title = cursor.getString(COLUMN_INDEX_TITLE);
final String summaryOn = cursor.getString(COLUMN_INDEX_SUMMARY_ON); final String summaryOn = cursor.getString(COLUMN_INDEX_SUMMARY_ON);
final String className = cursor.getString(COLUMN_INDEX_CLASS_NAME); final String className = cursor.getString(COLUMN_INDEX_CLASS_NAME);
final int rank = cursor.getInt(COLUMN_INDEX_RANK);
final String key = cursor.getString(COLUMN_INDEX_KEY); final String key = cursor.getString(COLUMN_INDEX_KEY);
final String iconResStr = cursor.getString(COLUMN_INDEX_ICON); final String iconResStr = cursor.getString(COLUMN_INDEX_ICON);
final int payloadType = cursor.getInt(COLUMN_INDEX_PAYLOAD_TYPE); final int payloadType = cursor.getInt(COLUMN_INDEX_PAYLOAD_TYPE);
@@ -109,9 +127,13 @@ class CursorToSearchResultConverter {
return null; return null;
} }
final List<String> breadcrumbs = getBreadcrumbs(cursor);
final int rank = getRank(breadcrumbs, baseRank);
final SearchResult.Builder builder = new SearchResult.Builder(); final SearchResult.Builder builder = new SearchResult.Builder();
builder.addTitle(title) builder.addTitle(title)
.addSummary(summaryOn) .addSummary(summaryOn)
.addBreadcrumbs(breadcrumbs)
.addRank(rank) .addRank(rank)
.addIcon(getIconForPackage(contextMap, pkgName, className, iconResStr)) .addIcon(getIconForPackage(contextMap, pkgName, className, iconResStr))
.addPayload(payload); .addPayload(payload);
@@ -187,4 +209,24 @@ class CursorToSearchResultConverter {
} }
return null; return null;
} }
private List<String> getBreadcrumbs(Cursor cursor) {
return new ArrayList<>();
}
/** Uses the breadcrumbs to determine the offset to the base rank.
* There are two checks
* A) If the query matches the highest level menu title
* B) If the query matches a subsequent menu title
*
* If the query matches A and B, the offset is 0.
* If the query matches A only, the offset is 1.
* If the query matches neither A nor B, the offset is 2.
* @param crumbs from the Information Architecture
* @param baseRank of the result. Lower if it's a better result.
* @return
*/
private int getRank(List<String> crumbs, int baseRank) {
return baseRank;
}
} }

View File

@@ -108,15 +108,15 @@ public class DatabaseIndexingManager {
public boolean fullIndex; public boolean fullIndex;
public UpdateData() { public UpdateData() {
dataToUpdate = new ArrayList<SearchIndexableData>(); dataToUpdate = new ArrayList<>();
dataToDelete = new ArrayList<SearchIndexableData>(); dataToDelete = new ArrayList<>();
nonIndexableKeys = new HashMap<String, List<String>>(); nonIndexableKeys = new HashMap<>();
} }
public UpdateData(DatabaseIndexingManager.UpdateData other) { public UpdateData(DatabaseIndexingManager.UpdateData other) {
dataToUpdate = new ArrayList<SearchIndexableData>(other.dataToUpdate); dataToUpdate = new ArrayList<>(other.dataToUpdate);
dataToDelete = new ArrayList<SearchIndexableData>(other.dataToDelete); dataToDelete = new ArrayList<>(other.dataToDelete);
nonIndexableKeys = new HashMap<String, List<String>>(other.nonIndexableKeys); nonIndexableKeys = new HashMap<>(other.nonIndexableKeys);
forceUpdate = other.forceUpdate; forceUpdate = other.forceUpdate;
fullIndex = other.fullIndex; fullIndex = other.fullIndex;
} }

View File

@@ -16,31 +16,19 @@
package com.android.settings.search2; package com.android.settings.search2;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Log;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.search.Index;
import com.android.settings.search.IndexDatabaseHelper; import com.android.settings.search.IndexDatabaseHelper;
import com.android.settings.utils.AsyncLoader; import com.android.settings.utils.AsyncLoader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns;
import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX;
/** /**
* AsyncTask to retrieve Settings, First party app and any intent based results. * AsyncTask to retrieve Settings, First party app and any intent based results.
@@ -54,30 +42,68 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
private final CursorToSearchResultConverter mConverter; private final CursorToSearchResultConverter mConverter;
/* These indices are used to match the columns of the this loader's SELECT statement. /* These indices are used to match the columns of the this loader's SELECT statement.
These are not necessarily the same order or coverage as the schema defined in These are not necessarily the same order nor similar coverage as the schema defined in
IndexDatabaseHelper */ IndexDatabaseHelper */
public static final int COLUMN_INDEX_RANK = 0; static final int COLUMN_INDEX_ID = 0;
public static final int COLUMN_INDEX_TITLE = 1; static final int COLUMN_INDEX_TITLE = 1;
public static final int COLUMN_INDEX_SUMMARY_ON = 2; static final int COLUMN_INDEX_SUMMARY_ON = 2;
public static final int COLUMN_INDEX_SUMMARY_OFF = 3; static final int COLUMN_INDEX_SUMMARY_OFF = 3;
public static final int COLUMN_INDEX_ENTRIES = 4; static final int COLUMN_INDEX_CLASS_NAME = 4;
public static final int COLUMN_INDEX_KEYWORDS = 5; static final int COLUMN_INDEX_SCREEN_TITLE = 5;
public static final int COLUMN_INDEX_CLASS_NAME = 6; static final int COLUMN_INDEX_ICON = 6;
public static final int COLUMN_INDEX_SCREEN_TITLE = 7; static final int COLUMN_INDEX_INTENT_ACTION = 7;
public static final int COLUMN_INDEX_ICON = 8; static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
public static final int COLUMN_INDEX_INTENT_ACTION = 9; static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 10; static final int COLUMN_INDEX_KEY = 10;
public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 11; static final int COLUMN_INDEX_PAYLOAD_TYPE = 11;
public static final int COLUMN_INDEX_ENABLED = 12; static final int COLUMN_INDEX_PAYLOAD = 12;
public static final int COLUMN_INDEX_KEY = 13;
public static final int COLUMN_INDEX_PAYLOAD_TYPE = 14; public static final String[] SELECT_COLUMNS = {
public static final int COLUMN_INDEX_PAYLOAD = 15; IndexColumns.DOCID,
IndexColumns.DATA_TITLE,
IndexColumns.DATA_SUMMARY_ON,
IndexColumns.DATA_SUMMARY_OFF,
IndexColumns.CLASS_NAME,
IndexColumns.SCREEN_TITLE,
IndexColumns.ICON,
IndexColumns.INTENT_ACTION,
IndexColumns.INTENT_TARGET_PACKAGE,
IndexColumns.INTENT_TARGET_CLASS,
IndexColumns.DATA_KEY_REF,
IndexColumns.PAYLOAD_TYPE,
IndexColumns.PAYLOAD
};
public static final String[] MATCH_COLUMNS_PRIMARY = {
IndexColumns.DATA_TITLE,
IndexColumns.DATA_TITLE_NORMALIZED,
};
public static final String[] MATCH_COLUMNS_SECONDARY = {
IndexColumns.DATA_SUMMARY_ON,
IndexColumns.DATA_SUMMARY_ON_NORMALIZED,
IndexColumns.DATA_SUMMARY_OFF,
IndexColumns.DATA_SUMMARY_OFF_NORMALIZED,
};
public static final String[] MATCH_COLUMNS_TERTIARY = {
IndexColumns.DATA_KEYWORDS,
IndexColumns.DATA_ENTRIES
};
/**
* Base ranks defines the best possible rank based on what the query matches.
* If the query matches the title, the best rank it can be is 1
* If the query only matches the summary, the best rank it can be is 4
* If the query only matches keywords or entries, the best rank it can be is 7
*/
private static final int[] BASE_RANKS = {1, 4, 7};
public DatabaseResultLoader(Context context, String queryText) { public DatabaseResultLoader(Context context, String queryText) {
super(context); super(context);
mDatabase = IndexDatabaseHelper.getInstance(context).getReadableDatabase(); mDatabase = IndexDatabaseHelper.getInstance(context).getReadableDatabase();
mQueryText = queryText; mQueryText = cleanQuery(queryText);
mConverter = new CursorToSearchResultConverter(context); mConverter = new CursorToSearchResultConverter(context, mQueryText);
} }
@Override @Override
@@ -91,10 +117,34 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
return null; return null;
} }
String query = getSQLQuery(); final List<SearchResult> primaryResults;
Cursor result = mDatabase.rawQuery(query, null); final List<SearchResult> secondaryResults;
final List<SearchResult> tertiaryResults;
return mConverter.convertCursor(result); primaryResults = query(MATCH_COLUMNS_PRIMARY, BASE_RANKS[0]);
secondaryResults = query(MATCH_COLUMNS_SECONDARY, BASE_RANKS[1]);
tertiaryResults = query(MATCH_COLUMNS_TERTIARY, BASE_RANKS[2]);
final List<SearchResult> results = new ArrayList<>(primaryResults.size()
+ secondaryResults.size()
+ tertiaryResults.size());
results.addAll(primaryResults);
results.addAll(secondaryResults);
results.addAll(tertiaryResults);
return results;
}
private List<SearchResult> query(String[] matchColumns, int baseRank) {
final String whereClause = buildWhereClause(matchColumns);
final String[] selection = new String[matchColumns.length];
final String query = "%" + mQueryText + "%";
Arrays.fill(selection, query);
final Cursor resultCursor = mDatabase.query(TABLE_PREFS_INDEX, SELECT_COLUMNS, whereClause,
selection, null, null, null);
return mConverter.convertCursor(resultCursor, baseRank);
} }
@Override @Override
@@ -103,17 +153,24 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
return super.onCancelLoad(); return super.onCancelLoad();
} }
protected String getSQLQuery() { /**
return String.format("SELECT data_rank, data_title, data_summary_on, " + * A generic method to make the query suitable for searching the database.
"data_summary_off, data_entries, data_keywords, class_name, screen_title," * @return the cleaned query string
+ " icon, " + */
"intent_action, intent_target_package, intent_target_class, enabled, " + private static String cleanQuery(String query) {
"data_key_reference, payload_type, payload FROM prefs_index WHERE prefs_index MATCH " return query.trim();
+ "'data_title:%s* " +
"OR data_title_normalized:%s* OR data_keywords:%s*' AND locale = 'en_US'",
mQueryText, mQueryText, mQueryText);
} }
private static String buildWhereClause(String[] matchColumns) {
StringBuilder sb = new StringBuilder(" ");
final int count = matchColumns.length;
for (int n = 0; n < count; n++) {
sb.append(matchColumns[n]);
sb.append(" like ?");
if (n < count - 1) {
sb.append(" OR ");
}
}
return sb.toString();
}
} }

View File

@@ -18,7 +18,7 @@ package com.android.settings.search2;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import java.util.ArrayList; import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -42,7 +42,7 @@ public class SearchResult implements Comparable<SearchResult> {
* An ordered list of the information hierarchy. * An ordered list of the information hierarchy.
* Intent Results: Displayed a hierarchy of selections to reach the setting from the home screen * Intent Results: Displayed a hierarchy of selections to reach the setting from the home screen
*/ */
public final ArrayList<String> breadcrumbs; public final List<String> breadcrumbs;
/** /**
* A suggestion for the ranking of the result. * A suggestion for the ranking of the result.
@@ -96,7 +96,7 @@ public class SearchResult implements Comparable<SearchResult> {
public static class Builder { public static class Builder {
protected CharSequence mTitle; protected CharSequence mTitle;
protected CharSequence mSummary; protected CharSequence mSummary;
protected ArrayList<String> mBreadcrumbs; protected List<String> mBreadcrumbs;
protected int mRank = 42; protected int mRank = 42;
protected ResultPayload mResultPayload; protected ResultPayload mResultPayload;
protected Drawable mIcon; protected Drawable mIcon;
@@ -111,7 +111,7 @@ public class SearchResult implements Comparable<SearchResult> {
return this; return this;
} }
public Builder addBreadcrumbs(ArrayList<String> breadcrumbs) { public Builder addBreadcrumbs(List<String> breadcrumbs) {
mBreadcrumbs = breadcrumbs; mBreadcrumbs = breadcrumbs;
return this; return this;
} }

View File

@@ -0,0 +1,246 @@
/*
* 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.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 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.lang.reflect.Field;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class DatabaseResultLoaderTest {
private Context mContext;
private DatabaseResultLoader loader;
SQLiteDatabase mDb;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
setUpDb();
}
@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();
}
}
@Test
public void testMatchTitle() {
loader = new DatabaseResultLoader(mContext, "title");
assertThat(loader.loadInBackground().size()).isEqualTo(3);
}
@Test
public void testMatchSummary() {
loader = new DatabaseResultLoader(mContext, "summary");
assertThat(loader.loadInBackground().size()).isEqualTo(3);
}
@Test
public void testMatchKeywords() {
loader = new DatabaseResultLoader(mContext, "keywords");
assertThat(loader.loadInBackground().size()).isEqualTo(3);
}
@Test
public void testMatchEntries() {
loader = new DatabaseResultLoader(mContext, "entries");
assertThat(loader.loadInBackground().size()).isEqualTo(3);
}
@Test
public void testSpecialCaseWord_MatchesNonPrefix() {
insertSpecialCase("Data usage");
loader = new DatabaseResultLoader(mContext, "usage");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
@Test
public void testSpecialCaseSpace_Matches() {
insertSpecialCase("space");
loader = new DatabaseResultLoader(mContext, " space ");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
@Test
public void testSpecialCaseDash_MatchesWordNoDash() {
insertSpecialCase("wi-fi calling");
loader = new DatabaseResultLoader(mContext, "wifi");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
@Test
public void testSpecialCaseDash_MatchesWordWithDash() {
insertSpecialCase("priorités seulment");
loader = new DatabaseResultLoader(mContext, "priorités");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
@Test
public void testSpecialCaseDash_MatchesWordWithoutDash() {
insertSpecialCase("priorités seulment");
loader = new DatabaseResultLoader(mContext, "priorites");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
@Test
public void testSpecialCaseDash_MatchesEntireQueryWithoutDash() {
insertSpecialCase("wi-fi calling");
loader = new DatabaseResultLoader(mContext, "wifi calling");
assertThat(loader.loadInBackground().size()).isEqualTo(1);
}
private void insertSpecialCase(String specialCase) {
String normalized = DatabaseIndexingUtils.normalizeHyphen(specialCase);
normalized = DatabaseIndexingUtils.normalizeString(normalized);
ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, 0);
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, "en-us");
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, specialCase);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, normalized);
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "");
values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME,
"com.android.settings.gestures.GestureSettings");
values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
}
private void setUpDb() {
ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, 0);
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, "en-us");
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, "alpha_title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, "alpha title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "alpha_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "alpha_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "alpha_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "alpha_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "alpha_entries");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "alpha_keywords");
values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME,
"com.android.settings.gestures.GestureSettings");
values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, 1);
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, "en-us");
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, "bravo_title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, "bravo title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "bravo_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "bravo_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "bravo_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "bravo_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "bravo_entries");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "bravo_keywords");
values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME,
"com.android.settings.gestures.GestureSettings");
values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, 2);
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, "en-us");
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, "charlie_title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, "charlie title");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "charlie_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "charlie_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "charlie_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "charlie_summary");
values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "charlie_entries");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "charlie_keywords");
values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME,
"com.android.settings.gestures.GestureSettings");
values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
}
}

View File

@@ -40,6 +40,8 @@ import org.robolectric.annotation.Config;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@@ -48,16 +50,32 @@ public class CursorToSearchResultConverterTest {
private CursorToSearchResultConverter mConverter; private CursorToSearchResultConverter mConverter;
private static final String[] COLUMNS = new String[]{"rank", "title", "summary_on", private static final String[] COLUMNS = new String[]{
"summary off", "entries", "keywords", "class name", "screen title", "icon", IndexColumns.DOCID,
"intent action", "target package", "target class", "enabled", "key", IndexColumns.DATA_TITLE,
"payload_type", "payload"}; IndexColumns.DATA_SUMMARY_ON,
IndexColumns.DATA_SUMMARY_OFF,
IndexColumns.CLASS_NAME,
IndexColumns.SCREEN_TITLE,
IndexColumns.ICON,
IndexColumns.INTENT_ACTION,
IndexColumns.INTENT_TARGET_PACKAGE,
IndexColumns.INTENT_TARGET_CLASS,
IndexColumns.DATA_KEY_REF,
IndexColumns.PAYLOAD_TYPE,
IndexColumns.PAYLOAD
};
private static final String[] TITLES = new String[]{"title1", "title2", "title3"}; private static final String ID = "id";
private static final String SUMMARY = "SUMMARY"; private static final String[] TITLES = {"title1", "title2", "title3"};
private static final String SUMMARY = "summary";
private static final String QUERY = "query";
private final int BASE_RANK = 1;
private static final int EXAMPLES = 3; private static final int EXAMPLES = 3;
private static final Intent mIntent = new Intent("com.android.settings"); private static final Intent mIntent = new Intent("com.android.settings");
private static final int mIcon = R.drawable.ic_search_history; private static final int mIcon = R.drawable.ic_search_history;
private List<SearchResult> mResults;
private Drawable mDrawable; private Drawable mDrawable;
@@ -65,32 +83,33 @@ public class CursorToSearchResultConverterTest {
public void setUp() { public void setUp() {
Context context = Robolectric.buildActivity(Activity.class).get(); Context context = Robolectric.buildActivity(Activity.class).get();
mDrawable = context.getDrawable(mIcon); mDrawable = context.getDrawable(mIcon);
mConverter = new CursorToSearchResultConverter(context); mResults = new ArrayList<>();
mConverter = new CursorToSearchResultConverter(context, QUERY);
} }
@Test @Test
public void testParseNullResults_ReturnsNull() { public void testParseNullResults_ReturnsNull() {
List<SearchResult> results = mConverter.convertCursor(null); List<SearchResult> results = mConverter.convertCursor(null, BASE_RANK);
assertThat(results).isNull(); assertThat(results).isNull();
} }
@Test @Test
public void testParseCursor_NotNull() { public void testParseCursor_NotNull() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor()); List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
assertThat(results).isNotNull(); assertThat(results).isNotNull();
} }
@Test @Test
public void testParseCursor_MatchesRank() { public void testParseCursor_MatchesRank() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor()); List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
for (int i = 0; i < EXAMPLES; i++) { for (int i = 0; i < EXAMPLES; i++) {
assertThat(results.get(i).rank).isEqualTo(i); assertThat(results.get(i).rank).isEqualTo(BASE_RANK);
} }
} }
@Test @Test
public void testParseCursor_MatchesTitle() { public void testParseCursor_MatchesTitle() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor()); List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
for (int i = 0; i < EXAMPLES; i++) { for (int i = 0; i < EXAMPLES; i++) {
assertThat(results.get(i).title).isEqualTo(TITLES[i]); assertThat(results.get(i).title).isEqualTo(TITLES[i]);
} }
@@ -98,7 +117,7 @@ public class CursorToSearchResultConverterTest {
@Test @Test
public void testParseCursor_MatchesSummary() { public void testParseCursor_MatchesSummary() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor()); List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
for (int i = 0; i < EXAMPLES; i++) { for (int i = 0; i < EXAMPLES; i++) {
assertThat(results.get(i).summary).isEqualTo(SUMMARY); assertThat(results.get(i).summary).isEqualTo(SUMMARY);
} }
@@ -106,7 +125,7 @@ public class CursorToSearchResultConverterTest {
@Test @Test
public void testParseCursor_MatchesIcon() { public void testParseCursor_MatchesIcon() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor()); List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
for (int i = 0; i < EXAMPLES; i++) { for (int i = 0; i < EXAMPLES; i++) {
Drawable resultDrawable = results.get(i).icon; Drawable resultDrawable = results.get(i).icon;
assertThat(resultDrawable.toString()).isEqualTo(mDrawable.toString()); assertThat(resultDrawable.toString()).isEqualTo(mDrawable.toString());
@@ -116,7 +135,7 @@ public class CursorToSearchResultConverterTest {
@Test @Test
public void testParseCursor_NoIcon() { public void testParseCursor_NoIcon() {
List<SearchResult> results = mConverter.convertCursor( List<SearchResult> results = mConverter.convertCursor(
getDummyCursor(false /* hasIcon */)); getDummyCursor(false /* hasIcon */), BASE_RANK);
for (int i = 0; i < EXAMPLES; i++) { for (int i = 0; i < EXAMPLES; i++) {
Drawable resultDrawable = results.get(i).icon; Drawable resultDrawable = results.get(i).icon;
assertThat(resultDrawable).isNull(); assertThat(resultDrawable).isNull();
@@ -125,7 +144,7 @@ public class CursorToSearchResultConverterTest {
@Test @Test
public void testParseCursor_MatchesPayloadType() { public void testParseCursor_MatchesPayloadType() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor()); List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
ResultPayload payload; ResultPayload payload;
for (int i = 0; i < EXAMPLES; i++) { for (int i = 0; i < EXAMPLES; i++) {
payload = results.get(i).payload; payload = results.get(i).payload;
@@ -138,24 +157,21 @@ public class CursorToSearchResultConverterTest {
MatrixCursor cursor = new MatrixCursor(COLUMNS); MatrixCursor cursor = new MatrixCursor(COLUMNS);
final String BLANK = ""; final String BLANK = "";
cursor.addRow(new Object[]{ cursor.addRow(new Object[]{
0, // rank ID, // Doc ID
TITLES[0], TITLES[0], // Title
SUMMARY, SUMMARY, // Summary on
SUMMARY, // summary off SUMMARY, // summary off
BLANK, // entries
BLANK, // Keywords
GestureSettings.class.getName(), GestureSettings.class.getName(),
BLANK, // screen title BLANK, // screen title
null, // icon null, // icon
BLANK, // action BLANK, // action
null, // target package null, // target package
BLANK, // target class BLANK, // target class
BLANK, // enabled BLANK, // Key
BLANK, // key
0, // Payload Type 0, // Payload Type
null // Payload null // Payload
}); });
List<SearchResult> results = mConverter.convertCursor(cursor); List<SearchResult> results = mConverter.convertCursor(cursor, BASE_RANK);
IntentPayload payload = (IntentPayload) results.get(0).payload; IntentPayload payload = (IntentPayload) results.get(0).payload;
Intent intent = payload.intent; Intent intent = payload.intent;
assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName()); assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
@@ -163,7 +179,7 @@ public class CursorToSearchResultConverterTest {
@Test @Test
public void testParseCursor_MatchesIntentPayload() { public void testParseCursor_MatchesIntentPayload() {
List<SearchResult> results = mConverter.convertCursor(getDummyCursor()); List<SearchResult> results = mConverter.convertCursor(getDummyCursor(), BASE_RANK);
IntentPayload payload; IntentPayload payload;
for (int i = 0; i < EXAMPLES; i++) { for (int i = 0; i < EXAMPLES; i++) {
payload = (IntentPayload) results.get(i).payload; payload = (IntentPayload) results.get(i).payload;
@@ -185,24 +201,21 @@ public class CursorToSearchResultConverterTest {
final InlineSwitchPayload payload = new InlineSwitchPayload(uri, source, map); final InlineSwitchPayload payload = new InlineSwitchPayload(uri, source, map);
cursor.addRow(new Object[]{ cursor.addRow(new Object[]{
0, // rank ID, // Doc ID
TITLES[0], TITLES[0], // Title
SUMMARY, SUMMARY, // Summary on
SUMMARY, // summary off SUMMARY, // summary off
BLANK, // entries
BLANK, // Keywords
GestureSettings.class.getName(), GestureSettings.class.getName(),
BLANK, // screen title BLANK, // screen title
null, // icon null, // icon
BLANK, // action BLANK, // action
null, // target package null, // target package
BLANK, // target class BLANK, // target class
BLANK, // enabled BLANK, // Key
BLANK, // key
type, // Payload Type type, // Payload Type
ResultPayloadUtils.marshall(payload) // Payload ResultPayloadUtils.marshall(payload) // Payload
}); });
List<SearchResult> results = mConverter.convertCursor(cursor); List<SearchResult> results = mConverter.convertCursor(cursor, BASE_RANK);
InlineSwitchPayload newPayload = (InlineSwitchPayload) results.get(0).payload; InlineSwitchPayload newPayload = (InlineSwitchPayload) results.get(0).payload;
assertThat(newPayload.settingsUri).isEqualTo(uri); assertThat(newPayload.settingsUri).isEqualTo(uri);
@@ -222,22 +235,17 @@ public class CursorToSearchResultConverterTest {
for (int i = 0; i < EXAMPLES; i++) { for (int i = 0; i < EXAMPLES; i++) {
ArrayList<String> item = new ArrayList<>(COLUMNS.length); ArrayList<String> item = new ArrayList<>(COLUMNS.length);
item.add(Integer.toString(i)); item.add(ID + i); // Doc ID
item.add(TITLES[i]); item.add(TITLES[i]); // Title
item.add(SUMMARY); item.add(SUMMARY); // Summary on
item.add(BLANK); // summary off item.add(BLANK); // summary off
item.add(BLANK); // entries
item.add(BLANK); // keywords
item.add(BLANK); // classname item.add(BLANK); // classname
item.add(BLANK); // screen title item.add(BLANK); // screen title
item.add(hasIcon ? Integer.toString(mIcon) : null); item.add(hasIcon ? Integer.toString(mIcon) : null); // Icon
item.add(mIntent.getAction()); item.add(mIntent.getAction()); // Intent action
item.add(BLANK); // target package item.add(BLANK); // target package
item.add(BLANK); // target class item.add(BLANK); // target class
item.add(BLANK); // enabled item.add(BLANK); // Key
item.add(BLANK); // key
// Note there is no user id. This is omitted because it is not being
// queried. Should the queries change, so should this method.
item.add(Integer.toString(0)); // Payload Type item.add(Integer.toString(0)); // Payload Type
item.add(null); // Payload item.add(null); // Payload

View File

@@ -46,7 +46,6 @@ import static org.mockito.Mockito.when;
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class InstalledAppResultLoaderTest { public class InstalledAppResultLoaderTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext; private Context mContext;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)