Merge "Move search indexing into a separate class"

This commit is contained in:
TreeHugger Robot
2017-09-13 19:43:38 +00:00
committed by Android (Google) Code Review
13 changed files with 1536 additions and 1324 deletions

View File

@@ -22,68 +22,33 @@ import static com.android.settings.search.DatabaseResultLoader
.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_KEY;
import static com.android.settings.search.DatabaseResultLoader.SELECT_COLUMNS;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.CLASS_NAME;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_ENTRIES;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEY_REF;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_RANK;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns
.DATA_SUMMARY_OFF_NORMALIZED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns
.DATA_SUMMARY_ON_NORMALIZED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DOCID;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ENABLED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ICON;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_ACTION;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.LOCALE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.PAYLOAD;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.SCREEN_TITLE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.USER_ID;
import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.AsyncTask;
import android.os.Build;
import android.provider.SearchIndexableData;
import android.provider.SearchIndexableResource;
import android.provider.SearchIndexablesContract;
import android.support.annotation.DrawableRes;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
import com.android.settings.SettingsActivity;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.indexing.IndexableDataCollector;
import com.android.settings.search.indexing.IndexDataConverter;
import com.android.settings.search.indexing.PreIndexData;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import com.android.settings.search.indexing.PreIndexDataCollector;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -91,7 +56,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* Consumes the SearchIndexableProvider content providers.
* Updates the Resource, Raw Data and non-indexable data for Search.
*
* TODO this class needs to be refactored by moving most of its methods into controllers
* TODO(b/33577327) this class needs to be refactored by moving most of its methods into controllers
*/
public class DatabaseIndexingManager {
@@ -103,14 +68,10 @@ public class DatabaseIndexingManager {
public static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER =
"SEARCH_INDEX_DATA_PROVIDER";
private static final String NODE_NAME_PREFERENCE_SCREEN = "PreferenceScreen";
private static final String NODE_NAME_CHECK_BOX_PREFERENCE = "CheckBoxPreference";
private static final String NODE_NAME_LIST_PREFERENCE = "ListPreference";
@VisibleForTesting
final AtomicBoolean mIsIndexingComplete = new AtomicBoolean(false);
private IndexableDataCollector mCollector;
private PreIndexDataCollector mCollector;
private Context mContext;
@@ -143,8 +104,8 @@ public class DatabaseIndexingManager {
final String providerVersionedNames =
IndexDatabaseHelper.buildProviderVersionedNames(providers);
final boolean isFullIndex = isFullIndex(mContext, localeStr,
fingerprint, providerVersionedNames);
final boolean isFullIndex = isFullIndex(mContext, localeStr, fingerprint,
providerVersionedNames);
if (isFullIndex) {
rebuildDatabase();
@@ -174,7 +135,7 @@ public class DatabaseIndexingManager {
@VisibleForTesting
PreIndexData getIndexDataFromProviders(List<ResolveInfo> providers, boolean isFullIndex) {
if (mCollector == null) {
mCollector = new IndexableDataCollector(mContext);
mCollector = new PreIndexDataCollector(mContext);
}
return mCollector.collectIndexableData(providers, isFullIndex);
}
@@ -191,7 +152,7 @@ public class DatabaseIndexingManager {
*/
@VisibleForTesting
boolean isFullIndex(Context context, String locale, String fingerprint,
String providerVersionedNames) {
String providerVersionedNames) {
final boolean isLocaleIndexed = IndexDatabaseHelper.isLocaleAlreadyIndexed(context, locale);
final boolean isBuildIndexed = IndexDatabaseHelper.isBuildIndexed(context, fingerprint);
final boolean areProvidersIndexed = IndexDatabaseHelper
@@ -222,7 +183,6 @@ public class DatabaseIndexingManager {
*/
@VisibleForTesting
void updateDatabase(PreIndexData indexData, boolean needsReindexing, String localeStr) {
final List<SearchIndexableData> dataToUpdate = indexData.dataToUpdate;
final Map<String, Set<String>> nonIndexableKeys = indexData.nonIndexableKeys;
final SQLiteDatabase database = getWritableDatabase();
@@ -235,9 +195,7 @@ public class DatabaseIndexingManager {
database.beginTransaction();
// Add new data from Providers at initial index time, or inserted later.
if (dataToUpdate.size() > 0) {
addDataToDatabase(database, localeStr, dataToUpdate, nonIndexableKeys);
}
addIndaxebleDataToDatabase(database, localeStr, indexData);
// Only check for non-indexable key updates after initial index.
// Enabled state with non-indexable keys is checked when items are first inserted.
@@ -251,32 +209,14 @@ public class DatabaseIndexingManager {
}
}
/**
* Inserts {@link SearchIndexableData} into the database.
*
* @param database where the data will be inserted.
* @param localeStr is the locale of the data to be inserted.
* @param dataToUpdate is a {@link List} of the data to be inserted.
* @param nonIndexableKeys is a {@link Map} from Package Name to a {@link Set} of keys which
* identify search results which should not be surfaced.
*/
@VisibleForTesting
void addDataToDatabase(SQLiteDatabase database, String localeStr,
List<SearchIndexableData> dataToUpdate, Map<String, Set<String>> nonIndexableKeys) {
final long current = System.currentTimeMillis();
for (SearchIndexableData data : dataToUpdate) {
try {
indexOneSearchIndexableData(database, localeStr, data, nonIndexableKeys);
} catch (Exception e) {
Log.e(LOG_TAG, "Cannot index: " + (data != null ? data.className : data)
+ " for locale: " + localeStr, e);
}
void addIndaxebleDataToDatabase(SQLiteDatabase database, String locale, PreIndexData data) {
if (data.dataToUpdate.size() == 0) {
return;
}
final long now = System.currentTimeMillis();
Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
(now - current) + " millis");
IndexDataConverter manager = new IndexDataConverter(mContext, database);
manager.addDataToDatabase(locale, data.dataToUpdate, data.nonIndexableKeys);
}
/**
@@ -388,646 +328,6 @@ public class DatabaseIndexingManager {
}
}
@VisibleForTesting
void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
SearchIndexableData data, Map<String, Set<String>> nonIndexableKeys) {
if (data instanceof SearchIndexableResource) {
indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);
} else if (data instanceof SearchIndexableRaw) {
indexOneRaw(database, localeStr, (SearchIndexableRaw) data, nonIndexableKeys);
}
}
private void indexOneRaw(SQLiteDatabase database, String localeStr,
SearchIndexableRaw raw, Map<String, Set<String>> nonIndexableKeysFromResource) {
// Should be the same locale as the one we are processing
if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
return;
}
Set<String> packageKeys = nonIndexableKeysFromResource.get(raw.intentTargetPackage);
boolean enabled = raw.enabled;
if (packageKeys != null && packageKeys.contains(raw.key)) {
enabled = false;
}
DatabaseRow.Builder builder = new DatabaseRow.Builder();
builder.setLocale(localeStr)
.setEntries(raw.entries)
.setClassName(raw.className)
.setScreenTitle(raw.screenTitle)
.setIconResId(raw.iconResId)
.setRank(raw.rank)
.setIntentAction(raw.intentAction)
.setIntentTargetPackage(raw.intentTargetPackage)
.setIntentTargetClass(raw.intentTargetClass)
.setEnabled(enabled)
.setKey(raw.key)
.setUserId(raw.userId);
updateOneRowWithFilteredData(database, builder, raw.title, raw.summaryOn, raw.summaryOff,
raw.keywords);
}
private void indexOneResource(SQLiteDatabase database, String localeStr,
SearchIndexableResource sir, Map<String, Set<String>> nonIndexableKeysFromResource) {
if (sir == null) {
Log.e(LOG_TAG, "Cannot index a null resource!");
return;
}
final List<String> nonIndexableKeys = new ArrayList<String>();
if (sir.xmlResId > SearchIndexableResources.NO_DATA_RES_ID) {
Set<String> resNonIndexableKeys = nonIndexableKeysFromResource.get(sir.packageName);
if (resNonIndexableKeys != null && resNonIndexableKeys.size() > 0) {
nonIndexableKeys.addAll(resNonIndexableKeys);
}
indexFromResource(database, localeStr, sir, nonIndexableKeys);
} else {
if (TextUtils.isEmpty(sir.className)) {
Log.w(LOG_TAG, "Cannot index an empty Search Provider name!");
return;
}
final Class<?> clazz = DatabaseIndexingUtils.getIndexableClass(sir.className);
if (clazz == null) {
Log.d(LOG_TAG, "SearchIndexableResource '" + sir.className +
"' should implement the " + Indexable.class.getName() + " interface!");
return;
}
// Will be non null only for a Local provider implementing a
// SEARCH_INDEX_DATA_PROVIDER field
final Indexable.SearchIndexProvider provider =
DatabaseIndexingUtils.getSearchIndexProvider(clazz);
if (provider != null) {
List<String> providerNonIndexableKeys = provider.getNonIndexableKeys(sir.context);
if (providerNonIndexableKeys != null && providerNonIndexableKeys.size() > 0) {
nonIndexableKeys.addAll(providerNonIndexableKeys);
}
indexFromProvider(database, localeStr, provider, sir, nonIndexableKeys);
}
}
}
@VisibleForTesting
void indexFromResource(SQLiteDatabase database, String localeStr,
SearchIndexableResource sir, List<String> nonIndexableKeys) {
final Context context = sir.context;
XmlResourceParser parser = null;
try {
parser = context.getResources().getXml(sir.xmlResId);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
// Parse next until start tag is found
}
String nodeName = parser.getName();
if (!NODE_NAME_PREFERENCE_SCREEN.equals(nodeName)) {
throw new RuntimeException(
"XML document must start with <PreferenceScreen> tag; found"
+ nodeName + " at " + parser.getPositionDescription());
}
final int outerDepth = parser.getDepth();
final AttributeSet attrs = Xml.asAttributeSet(parser);
final String screenTitle = XmlParserUtils.getDataTitle(context, attrs);
String key = XmlParserUtils.getDataKey(context, attrs);
String title;
String headerTitle;
String summary;
String headerSummary;
String keywords;
String headerKeywords;
String childFragment;
@DrawableRes
int iconResId;
ResultPayload payload;
boolean enabled;
final String fragmentName = sir.className;
final int rank = sir.rank;
final String intentAction = sir.intentAction;
final String intentTargetPackage = sir.intentTargetPackage;
final String intentTargetClass = sir.intentTargetClass;
Map<String, PreferenceControllerMixin> controllerUriMap = null;
if (fragmentName != null) {
controllerUriMap = DatabaseIndexingUtils
.getPreferenceControllerUriMap(fragmentName, context);
}
// Insert rows for the main PreferenceScreen node. Rewrite the data for removing
// hyphens.
headerTitle = XmlParserUtils.getDataTitle(context, attrs);
headerSummary = XmlParserUtils.getDataSummary(context, attrs);
headerKeywords = XmlParserUtils.getDataKeywords(context, attrs);
enabled = !nonIndexableKeys.contains(key);
// TODO: Set payload type for header results
DatabaseRow.Builder headerBuilder = new DatabaseRow.Builder();
headerBuilder.setLocale(localeStr)
.setEntries(null)
.setClassName(fragmentName)
.setScreenTitle(screenTitle)
.setRank(rank)
.setIntentAction(intentAction)
.setIntentTargetPackage(intentTargetPackage)
.setIntentTargetClass(intentTargetClass)
.setEnabled(enabled)
.setKey(key)
.setUserId(-1 /* default user id */);
// Flag for XML headers which a child element's title.
boolean isHeaderUnique = true;
DatabaseRow.Builder builder;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
nodeName = parser.getName();
title = XmlParserUtils.getDataTitle(context, attrs);
key = XmlParserUtils.getDataKey(context, attrs);
enabled = !nonIndexableKeys.contains(key);
keywords = XmlParserUtils.getDataKeywords(context, attrs);
iconResId = XmlParserUtils.getDataIcon(context, attrs);
if (isHeaderUnique && TextUtils.equals(headerTitle, title)) {
isHeaderUnique = false;
}
builder = new DatabaseRow.Builder();
builder.setLocale(localeStr)
.setClassName(fragmentName)
.setScreenTitle(screenTitle)
.setIconResId(iconResId)
.setRank(rank)
.setIntentAction(intentAction)
.setIntentTargetPackage(intentTargetPackage)
.setIntentTargetClass(intentTargetClass)
.setEnabled(enabled)
.setKey(key)
.setUserId(-1 /* default user id */);
if (!nodeName.equals(NODE_NAME_CHECK_BOX_PREFERENCE)) {
summary = XmlParserUtils.getDataSummary(context, attrs);
String entries = null;
if (nodeName.endsWith(NODE_NAME_LIST_PREFERENCE)) {
entries = XmlParserUtils.getDataEntries(context, attrs);
}
// TODO (b/62254931) index primitives instead of payload
payload = DatabaseIndexingUtils.getPayloadFromUriMap(controllerUriMap, key);
childFragment = XmlParserUtils.getDataChildFragment(context, attrs);
builder.setEntries(entries)
.setChildClassName(childFragment)
.setPayload(payload);
// Insert rows for the child nodes of PreferenceScreen
updateOneRowWithFilteredData(database, builder, title, summary,
null /* summary off */, keywords);
} else {
String summaryOn = XmlParserUtils.getDataSummaryOn(context, attrs);
String summaryOff = XmlParserUtils.getDataSummaryOff(context, attrs);
if (TextUtils.isEmpty(summaryOn) && TextUtils.isEmpty(summaryOff)) {
summaryOn = XmlParserUtils.getDataSummary(context, attrs);
}
updateOneRowWithFilteredData(database, builder, title, summaryOn, summaryOff,
keywords);
}
}
// The xml header's title does not match the title of one of the child settings.
if (isHeaderUnique) {
updateOneRowWithFilteredData(database, headerBuilder, headerTitle, headerSummary,
null /* summary off */, headerKeywords);
}
} catch (XmlPullParserException e) {
throw new RuntimeException("Error parsing PreferenceScreen", e);
} catch (IOException e) {
throw new RuntimeException("Error parsing PreferenceScreen", e);
} finally {
if (parser != null) parser.close();
}
}
private void indexFromProvider(SQLiteDatabase database, String localeStr,
Indexable.SearchIndexProvider provider, SearchIndexableResource sir,
List<String> nonIndexableKeys) {
final String className = sir.className;
final String intentAction = sir.intentAction;
final String intentTargetPackage = sir.intentTargetPackage;
if (provider == null) {
Log.w(LOG_TAG, "Cannot find provider: " + className);
return;
}
final List<SearchIndexableRaw> rawList = provider.getRawDataToIndex(mContext,
true /* enabled */);
if (rawList != null) {
final int rawSize = rawList.size();
for (int i = 0; i < rawSize; i++) {
SearchIndexableRaw raw = rawList.get(i);
// Should be the same locale as the one we are processing
if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
continue;
}
boolean enabled = !nonIndexableKeys.contains(raw.key);
DatabaseRow.Builder builder = new DatabaseRow.Builder();
builder.setLocale(localeStr)
.setEntries(raw.entries)
.setClassName(className)
.setScreenTitle(raw.screenTitle)
.setIconResId(raw.iconResId)
.setIntentAction(raw.intentAction)
.setIntentTargetPackage(raw.intentTargetPackage)
.setIntentTargetClass(raw.intentTargetClass)
.setEnabled(enabled)
.setKey(raw.key)
.setUserId(raw.userId);
updateOneRowWithFilteredData(database, builder, raw.title, raw.summaryOn,
raw.summaryOff, raw.keywords);
}
}
final List<SearchIndexableResource> resList =
provider.getXmlResourcesToIndex(mContext, true);
if (resList != null) {
final int resSize = resList.size();
for (int i = 0; i < resSize; i++) {
SearchIndexableResource item = resList.get(i);
// Should be the same locale as the one we are processing
if (!item.locale.toString().equalsIgnoreCase(localeStr)) {
continue;
}
item.className = TextUtils.isEmpty(item.className)
? className
: item.className;
item.intentAction = TextUtils.isEmpty(item.intentAction)
? intentAction
: item.intentAction;
item.intentTargetPackage = TextUtils.isEmpty(item.intentTargetPackage)
? intentTargetPackage
: item.intentTargetPackage;
indexFromResource(database, localeStr, item, nonIndexableKeys);
}
}
}
private void updateOneRowWithFilteredData(SQLiteDatabase database, DatabaseRow.Builder builder,
String title, String summaryOn, String summaryOff, String keywords) {
final String updatedTitle = DatabaseIndexingUtils.normalizeHyphen(title);
final String updatedSummaryOn = DatabaseIndexingUtils.normalizeHyphen(summaryOn);
final String updatedSummaryOff = DatabaseIndexingUtils.normalizeHyphen(summaryOff);
final String normalizedTitle = DatabaseIndexingUtils.normalizeString(updatedTitle);
final String normalizedSummaryOn = DatabaseIndexingUtils.normalizeString(updatedSummaryOn);
final String normalizedSummaryOff = DatabaseIndexingUtils
.normalizeString(updatedSummaryOff);
final String spaceDelimitedKeywords = DatabaseIndexingUtils.normalizeKeywords(keywords);
builder.setUpdatedTitle(updatedTitle)
.setUpdatedSummaryOn(updatedSummaryOn)
.setUpdatedSummaryOff(updatedSummaryOff)
.setNormalizedTitle(normalizedTitle)
.setNormalizedSummaryOn(normalizedSummaryOn)
.setNormalizedSummaryOff(normalizedSummaryOff)
.setSpaceDelimitedKeywords(spaceDelimitedKeywords);
updateOneRow(database, builder.build(mContext));
}
private void updateOneRow(SQLiteDatabase database, DatabaseRow row) {
if (TextUtils.isEmpty(row.updatedTitle)) {
return;
}
ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, row.getDocId());
values.put(LOCALE, row.locale);
values.put(DATA_RANK, row.rank);
values.put(DATA_TITLE, row.updatedTitle);
values.put(DATA_TITLE_NORMALIZED, row.normalizedTitle);
values.put(DATA_SUMMARY_ON, row.updatedSummaryOn);
values.put(DATA_SUMMARY_ON_NORMALIZED, row.normalizedSummaryOn);
values.put(DATA_SUMMARY_OFF, row.updatedSummaryOff);
values.put(DATA_SUMMARY_OFF_NORMALIZED, row.normalizedSummaryOff);
values.put(DATA_ENTRIES, row.entries);
values.put(DATA_KEYWORDS, row.spaceDelimitedKeywords);
values.put(CLASS_NAME, row.className);
values.put(SCREEN_TITLE, row.screenTitle);
values.put(INTENT_ACTION, row.intentAction);
values.put(INTENT_TARGET_PACKAGE, row.intentTargetPackage);
values.put(INTENT_TARGET_CLASS, row.intentTargetClass);
values.put(ICON, row.iconResId);
values.put(ENABLED, row.enabled);
values.put(DATA_KEY_REF, row.key);
values.put(USER_ID, row.userId);
values.put(PAYLOAD_TYPE, row.payloadType);
values.put(PAYLOAD, row.payload);
database.replaceOrThrow(TABLE_PREFS_INDEX, null, values);
if (!TextUtils.isEmpty(row.className) && !TextUtils.isEmpty(row.childClassName)) {
ContentValues siteMapPair = new ContentValues();
final int pairDocId = Objects.hash(row.className, row.childClassName);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.DOCID, pairDocId);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.PARENT_CLASS, row.className);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.PARENT_TITLE, row.screenTitle);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.CHILD_CLASS, row.childClassName);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.CHILD_TITLE, row.updatedTitle);
database.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_SITE_MAP, null, siteMapPair);
}
}
public static class DatabaseRow {
public final String locale;
public final String updatedTitle;
public final String normalizedTitle;
public final String updatedSummaryOn;
public final String normalizedSummaryOn;
public final String updatedSummaryOff;
public final String normalizedSummaryOff;
public final String entries;
public final String className;
public final String childClassName;
public final String screenTitle;
public final int iconResId;
public final int rank;
public final String spaceDelimitedKeywords;
public final String intentAction;
public final String intentTargetPackage;
public final String intentTargetClass;
public final boolean enabled;
public final String key;
public final int userId;
public final int payloadType;
public final byte[] payload;
private DatabaseRow(Builder builder) {
locale = builder.mLocale;
updatedTitle = builder.mUpdatedTitle;
normalizedTitle = builder.mNormalizedTitle;
updatedSummaryOn = builder.mUpdatedSummaryOn;
normalizedSummaryOn = builder.mNormalizedSummaryOn;
updatedSummaryOff = builder.mUpdatedSummaryOff;
normalizedSummaryOff = builder.mNormalizedSummaryOff;
entries = builder.mEntries;
className = builder.mClassName;
childClassName = builder.mChildClassName;
screenTitle = builder.mScreenTitle;
iconResId = builder.mIconResId;
rank = builder.mRank;
spaceDelimitedKeywords = builder.mSpaceDelimitedKeywords;
intentAction = builder.mIntentAction;
intentTargetPackage = builder.mIntentTargetPackage;
intentTargetClass = builder.mIntentTargetClass;
enabled = builder.mEnabled;
key = builder.mKey;
userId = builder.mUserId;
payloadType = builder.mPayloadType;
payload = builder.mPayload != null ? ResultPayloadUtils.marshall(builder.mPayload)
: null;
}
/**
* Returns the doc id for this row.
*/
public int getDocId() {
// Eventually we want all DocIds to be the data_reference key. For settings values,
// this will be preference keys, and for non-settings they should be unique.
return TextUtils.isEmpty(key)
? Objects.hash(updatedTitle, className, screenTitle, intentTargetClass)
: key.hashCode();
}
public static class Builder {
private String mLocale;
private String mUpdatedTitle;
private String mNormalizedTitle;
private String mUpdatedSummaryOn;
private String mNormalizedSummaryOn;
private String mUpdatedSummaryOff;
private String mNormalizedSummaryOff;
private String mEntries;
private String mClassName;
private String mChildClassName;
private String mScreenTitle;
private int mIconResId;
private int mRank;
private String mSpaceDelimitedKeywords;
private String mIntentAction;
private String mIntentTargetPackage;
private String mIntentTargetClass;
private boolean mEnabled;
private String mKey;
private int mUserId;
@ResultPayload.PayloadType
private int mPayloadType;
private ResultPayload mPayload;
public Builder setLocale(String locale) {
mLocale = locale;
return this;
}
public Builder setUpdatedTitle(String updatedTitle) {
mUpdatedTitle = updatedTitle;
return this;
}
public Builder setNormalizedTitle(String normalizedTitle) {
mNormalizedTitle = normalizedTitle;
return this;
}
public Builder setUpdatedSummaryOn(String updatedSummaryOn) {
mUpdatedSummaryOn = updatedSummaryOn;
return this;
}
public Builder setNormalizedSummaryOn(String normalizedSummaryOn) {
mNormalizedSummaryOn = normalizedSummaryOn;
return this;
}
public Builder setUpdatedSummaryOff(String updatedSummaryOff) {
mUpdatedSummaryOff = updatedSummaryOff;
return this;
}
public Builder setNormalizedSummaryOff(String normalizedSummaryOff) {
this.mNormalizedSummaryOff = normalizedSummaryOff;
return this;
}
public Builder setEntries(String entries) {
mEntries = entries;
return this;
}
public Builder setClassName(String className) {
mClassName = className;
return this;
}
public Builder setChildClassName(String childClassName) {
mChildClassName = childClassName;
return this;
}
public Builder setScreenTitle(String screenTitle) {
mScreenTitle = screenTitle;
return this;
}
public Builder setIconResId(int iconResId) {
mIconResId = iconResId;
return this;
}
public Builder setRank(int rank) {
mRank = rank;
return this;
}
public Builder setSpaceDelimitedKeywords(String spaceDelimitedKeywords) {
mSpaceDelimitedKeywords = spaceDelimitedKeywords;
return this;
}
public Builder setIntentAction(String intentAction) {
mIntentAction = intentAction;
return this;
}
public Builder setIntentTargetPackage(String intentTargetPackage) {
mIntentTargetPackage = intentTargetPackage;
return this;
}
public Builder setIntentTargetClass(String intentTargetClass) {
mIntentTargetClass = intentTargetClass;
return this;
}
public Builder setEnabled(boolean enabled) {
mEnabled = enabled;
return this;
}
public Builder setKey(String key) {
mKey = key;
return this;
}
public Builder setUserId(int userId) {
mUserId = userId;
return this;
}
public Builder setPayload(ResultPayload payload) {
mPayload = payload;
if (mPayload != null) {
setPayloadType(mPayload.getType());
}
return this;
}
/**
* Payload type is added when a Payload is added to the Builder in {setPayload}
*
* @param payloadType PayloadType
* @return The Builder
*/
private Builder setPayloadType(@ResultPayload.PayloadType int payloadType) {
mPayloadType = payloadType;
return this;
}
/**
* Adds intent to inline payloads, or creates an Intent Payload as a fallback if the
* payload is null.
*/
private void setIntent(Context context) {
if (mPayload != null) {
return;
}
final Intent intent = buildIntent(context);
mPayload = new ResultPayload(intent);
mPayloadType = ResultPayload.PayloadType.INTENT;
}
/**
* Adds Intent payload to builder.
*/
private Intent buildIntent(Context context) {
final Intent intent;
boolean isEmptyIntentAction = TextUtils.isEmpty(mIntentAction);
// No intent action is set, or the intent action is for a subsetting.
if (isEmptyIntentAction
|| (!isEmptyIntentAction && TextUtils.equals(mIntentTargetPackage,
SearchIndexableResources.SUBSETTING_TARGET_PACKAGE))) {
// Action is null, we will launch it as a sub-setting
intent = DatabaseIndexingUtils.buildSubsettingIntent(context, mClassName, mKey,
mScreenTitle);
} else {
intent = new Intent(mIntentAction);
final String targetClass = mIntentTargetClass;
if (!TextUtils.isEmpty(mIntentTargetPackage)
&& !TextUtils.isEmpty(targetClass)) {
final ComponentName component = new ComponentName(mIntentTargetPackage,
targetClass);
intent.setComponent(component);
}
intent.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, mKey);
}
return intent;
}
public DatabaseRow build(Context context) {
setIntent(context);
return new DatabaseRow(this);
}
}
}
public class IndexingTask extends AsyncTask<Void, Void, Void> {
@VisibleForTesting

View File

@@ -174,18 +174,18 @@ public class DatabaseIndexingUtils {
return null;
}
static String normalizeHyphen(String input) {
public static String normalizeHyphen(String input) {
return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY;
}
static String normalizeString(String input) {
public static String normalizeString(String input) {
final String nohyphen = (input != null) ? input.replaceAll(HYPHEN, EMPTY) : EMPTY;
final String normalized = Normalizer.normalize(nohyphen, Normalizer.Form.NFD);
return REMOVE_DIACRITICALS_PATTERN.matcher(normalized).replaceAll("").toLowerCase();
}
static String normalizeKeywords(String input) {
public static String normalizeKeywords(String input) {
return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY;
}
}

View File

@@ -35,8 +35,7 @@ public abstract class InlinePayload extends ResultPayload {
/**
* Defines the key to access and store the Setting the inline result represents.
*/
@VisibleForTesting
final String mSettingKey;
private final String mSettingKey;
/**
* Defines where the Setting is stored.
@@ -154,4 +153,8 @@ public abstract class InlinePayload extends ResultPayload {
return false;
}
public String getKey() {
return mSettingKey;
}
}

View File

@@ -0,0 +1,287 @@
/*
* 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.indexing;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.android.settings.SettingsActivity;
import com.android.settings.search.DatabaseIndexingUtils;
import com.android.settings.search.ResultPayload;
import com.android.settings.search.ResultPayloadUtils;
import com.android.settings.search.SearchIndexableResources;
import java.util.Objects;
/**
* Data class representing a single row in the Setting Search results database.
*/
public class IndexData {
public final String locale;
public final String updatedTitle;
public final String normalizedTitle;
public final String updatedSummaryOn;
public final String normalizedSummaryOn;
public final String updatedSummaryOff;
public final String normalizedSummaryOff;
public final String entries;
public final String className;
public final String childClassName;
public final String screenTitle;
public final int iconResId;
public final int rank;
public final String spaceDelimitedKeywords;
public final String intentAction;
public final String intentTargetPackage;
public final String intentTargetClass;
public final boolean enabled;
public final String key;
public final int userId;
public final int payloadType;
public final byte[] payload;
private IndexData(Builder builder) {
locale = builder.mLocale;
updatedTitle = builder.mUpdatedTitle;
normalizedTitle = builder.mNormalizedTitle;
updatedSummaryOn = builder.mUpdatedSummaryOn;
normalizedSummaryOn = builder.mNormalizedSummaryOn;
updatedSummaryOff = builder.mUpdatedSummaryOff;
normalizedSummaryOff = builder.mNormalizedSummaryOff;
entries = builder.mEntries;
className = builder.mClassName;
childClassName = builder.mChildClassName;
screenTitle = builder.mScreenTitle;
iconResId = builder.mIconResId;
rank = builder.mRank;
spaceDelimitedKeywords = builder.mSpaceDelimitedKeywords;
intentAction = builder.mIntentAction;
intentTargetPackage = builder.mIntentTargetPackage;
intentTargetClass = builder.mIntentTargetClass;
enabled = builder.mEnabled;
key = builder.mKey;
userId = builder.mUserId;
payloadType = builder.mPayloadType;
payload = builder.mPayload != null ? ResultPayloadUtils.marshall(builder.mPayload)
: null;
}
/**
* Returns the doc id for this row.
*/
public int getDocId() {
// Eventually we want all DocIds to be the data_reference key. For settings values,
// this will be preference keys, and for non-settings they should be unique.
return TextUtils.isEmpty(key)
? Objects.hash(updatedTitle, className, screenTitle, intentTargetClass)
: key.hashCode();
}
public static class Builder {
private String mLocale;
private String mUpdatedTitle;
private String mNormalizedTitle;
private String mUpdatedSummaryOn;
private String mNormalizedSummaryOn;
private String mUpdatedSummaryOff;
private String mNormalizedSummaryOff;
private String mEntries;
private String mClassName;
private String mChildClassName;
private String mScreenTitle;
private int mIconResId;
private int mRank;
private String mSpaceDelimitedKeywords;
private String mIntentAction;
private String mIntentTargetPackage;
private String mIntentTargetClass;
private boolean mEnabled;
private String mKey;
private int mUserId;
@ResultPayload.PayloadType
private int mPayloadType;
private ResultPayload mPayload;
public Builder setLocale(String locale) {
mLocale = locale;
return this;
}
public Builder setUpdatedTitle(String updatedTitle) {
mUpdatedTitle = updatedTitle;
return this;
}
public Builder setNormalizedTitle(String normalizedTitle) {
mNormalizedTitle = normalizedTitle;
return this;
}
public Builder setUpdatedSummaryOn(String updatedSummaryOn) {
mUpdatedSummaryOn = updatedSummaryOn;
return this;
}
public Builder setNormalizedSummaryOn(String normalizedSummaryOn) {
mNormalizedSummaryOn = normalizedSummaryOn;
return this;
}
public Builder setUpdatedSummaryOff(String updatedSummaryOff) {
mUpdatedSummaryOff = updatedSummaryOff;
return this;
}
public Builder setNormalizedSummaryOff(String normalizedSummaryOff) {
this.mNormalizedSummaryOff = normalizedSummaryOff;
return this;
}
public Builder setEntries(String entries) {
mEntries = entries;
return this;
}
public Builder setClassName(String className) {
mClassName = className;
return this;
}
public Builder setChildClassName(String childClassName) {
mChildClassName = childClassName;
return this;
}
public Builder setScreenTitle(String screenTitle) {
mScreenTitle = screenTitle;
return this;
}
public Builder setIconResId(int iconResId) {
mIconResId = iconResId;
return this;
}
public Builder setRank(int rank) {
mRank = rank;
return this;
}
public Builder setSpaceDelimitedKeywords(String spaceDelimitedKeywords) {
mSpaceDelimitedKeywords = spaceDelimitedKeywords;
return this;
}
public Builder setIntentAction(String intentAction) {
mIntentAction = intentAction;
return this;
}
public Builder setIntentTargetPackage(String intentTargetPackage) {
mIntentTargetPackage = intentTargetPackage;
return this;
}
public Builder setIntentTargetClass(String intentTargetClass) {
mIntentTargetClass = intentTargetClass;
return this;
}
public Builder setEnabled(boolean enabled) {
mEnabled = enabled;
return this;
}
public Builder setKey(String key) {
mKey = key;
return this;
}
public Builder setUserId(int userId) {
mUserId = userId;
return this;
}
public Builder setPayload(ResultPayload payload) {
mPayload = payload;
if (mPayload != null) {
setPayloadType(mPayload.getType());
}
return this;
}
/**
* Payload type is added when a Payload is added to the Builder in {setPayload}
*
* @param payloadType PayloadType
* @return The Builder
*/
private Builder setPayloadType(@ResultPayload.PayloadType int payloadType) {
mPayloadType = payloadType;
return this;
}
/**
* Adds intent to inline payloads, or creates an Intent Payload as a fallback if the
* payload is null.
*/
private void setIntent(Context context) {
if (mPayload != null) {
return;
}
final Intent intent = buildIntent(context);
mPayload = new ResultPayload(intent);
mPayloadType = ResultPayload.PayloadType.INTENT;
}
/**
* Adds Intent payload to builder.
*/
private Intent buildIntent(Context context) {
final Intent intent;
boolean isEmptyIntentAction = TextUtils.isEmpty(mIntentAction);
// No intent action is set, or the intent action is for a subsetting.
if (isEmptyIntentAction
|| (!isEmptyIntentAction && TextUtils.equals(mIntentTargetPackage,
SearchIndexableResources.SUBSETTING_TARGET_PACKAGE))) {
// Action is null, we will launch it as a sub-setting
intent = DatabaseIndexingUtils.buildSubsettingIntent(context, mClassName, mKey,
mScreenTitle);
} else {
intent = new Intent(mIntentAction);
final String targetClass = mIntentTargetClass;
if (!TextUtils.isEmpty(mIntentTargetPackage)
&& !TextUtils.isEmpty(targetClass)) {
final ComponentName component = new ComponentName(mIntentTargetPackage,
targetClass);
intent.setComponent(component);
}
intent.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, mKey);
}
return intent;
}
public IndexData build(Context context) {
setIntent(context);
return new IndexData(this);
}
}
}

View File

@@ -0,0 +1,510 @@
/*
* 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.indexing;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.database.sqlite.SQLiteDatabase;
import android.provider.SearchIndexableData;
import android.provider.SearchIndexableResource;
import android.support.annotation.DrawableRes;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.search.DatabaseIndexingUtils;
import com.android.settings.search.IndexDatabaseHelper;
import com.android.settings.search.Indexable;
import com.android.settings.search.ResultPayload;
import com.android.settings.search.SearchIndexableRaw;
import com.android.settings.search.SearchIndexableResources;
import com.android.settings.search.XmlParserUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.CLASS_NAME;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_ENTRIES;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEY_REF;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_RANK;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ENABLED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ICON;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_ACTION;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.LOCALE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.PAYLOAD;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.SCREEN_TITLE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.USER_ID;
import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX;
/**
* Helper class to convert {@link PreIndexData} to {@link IndexData}.
*
* TODO (b/33577327) This is just copied straight from DatabaseIndexingManager. But it's still ugly.
* TODO This is currently a long chain of method calls. It needs to be broken up.
* TODO but for the sake of easy code reviews, that will happen later.
*/
public class IndexDataConverter {
private static final String LOG_TAG = "IndexDataConverter";
private static final String NODE_NAME_PREFERENCE_SCREEN = "PreferenceScreen";
private static final String NODE_NAME_CHECK_BOX_PREFERENCE = "CheckBoxPreference";
private static final String NODE_NAME_LIST_PREFERENCE = "ListPreference";
private Context mContext;
private SQLiteDatabase mDb;
public IndexDataConverter(Context context, SQLiteDatabase database) {
mContext = context;
mDb = database;
}
/**
* Inserts {@link SearchIndexableData} into the database.
*
* @param localeStr is the locale of the data to be inserted.
* @param dataToUpdate is a {@link List} of the data to be inserted.
* @param nonIndexableKeys is a {@link Map} from Package Name to a {@link Set} of keys which
* identify search results which should not be surfaced.
*/
public void addDataToDatabase(String localeStr, List<SearchIndexableData> dataToUpdate,
Map<String, Set<String>> nonIndexableKeys) {
final long current = System.currentTimeMillis();
for (SearchIndexableData data : dataToUpdate) {
try {
indexOneSearchIndexableData(localeStr, data, nonIndexableKeys);
} catch (Exception e) {
Log.e(LOG_TAG, "Cannot index: " + (data != null ? data.className : data)
+ " for locale: " + localeStr, e);
}
}
final long now = System.currentTimeMillis();
Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
(now - current) + " millis");
}
@VisibleForTesting
void indexOneSearchIndexableData(String localeStr, SearchIndexableData data,
Map<String, Set<String>> nonIndexableKeys) {
if (data instanceof SearchIndexableResource) {
indexOneResource(localeStr, (SearchIndexableResource) data, nonIndexableKeys);
} else if (data instanceof SearchIndexableRaw) {
indexOneRaw(localeStr, (SearchIndexableRaw) data, nonIndexableKeys);
}
}
@VisibleForTesting
void indexOneRaw(String localeStr, SearchIndexableRaw raw, Map<String,
Set<String>> nonIndexableKeysFromResource) {
// Should be the same locale as the one we are processing
if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
return;
}
Set<String> packageKeys = nonIndexableKeysFromResource.get(raw.intentTargetPackage);
boolean enabled = raw.enabled;
if (packageKeys != null && packageKeys.contains(raw.key)) {
enabled = false;
}
IndexData.Builder builder = new IndexData.Builder();
builder.setLocale(localeStr)
.setEntries(raw.entries)
.setClassName(raw.className)
.setScreenTitle(raw.screenTitle)
.setIconResId(raw.iconResId)
.setRank(raw.rank)
.setIntentAction(raw.intentAction)
.setIntentTargetPackage(raw.intentTargetPackage)
.setIntentTargetClass(raw.intentTargetClass)
.setEnabled(enabled)
.setKey(raw.key)
.setUserId(raw.userId);
updateOneRowWithFilteredData(builder, raw.title, raw.summaryOn, raw.summaryOff,
raw.keywords);
}
@VisibleForTesting
void indexOneResource(String localeStr, SearchIndexableResource sir,
Map<String, Set<String>> nonIndexableKeysFromResource) {
if (sir == null) {
Log.e(LOG_TAG, "Cannot index a null resource!");
return;
}
final List<String> nonIndexableKeys = new ArrayList<>();
if (sir.xmlResId > SearchIndexableResources.NO_DATA_RES_ID) {
Set<String> resNonIndexableKeys = nonIndexableKeysFromResource.get(sir.packageName);
if (resNonIndexableKeys != null && resNonIndexableKeys.size() > 0) {
nonIndexableKeys.addAll(resNonIndexableKeys);
}
indexFromResource(localeStr, sir, nonIndexableKeys);
} else {
if (TextUtils.isEmpty(sir.className)) {
Log.w(LOG_TAG, "Cannot index an empty Search Provider name!");
return;
}
final Class<?> clazz = DatabaseIndexingUtils.getIndexableClass(sir.className);
if (clazz == null) {
Log.d(LOG_TAG, "SearchIndexableResource '" + sir.className +
"' should implement the " + Indexable.class.getName() + " interface!");
return;
}
// Will be non null only for a Local provider implementing a
// SEARCH_INDEX_DATA_PROVIDER field
final Indexable.SearchIndexProvider provider =
DatabaseIndexingUtils.getSearchIndexProvider(clazz);
if (provider != null) {
List<String> providerNonIndexableKeys = provider.getNonIndexableKeys(sir.context);
if (providerNonIndexableKeys != null && providerNonIndexableKeys.size() > 0) {
nonIndexableKeys.addAll(providerNonIndexableKeys);
}
indexFromProvider(localeStr, provider, sir, nonIndexableKeys);
}
}
}
@VisibleForTesting
void indexFromResource(String localeStr, SearchIndexableResource sir,
List<String> nonIndexableKeys) {
final Context context = sir.context;
XmlResourceParser parser = null;
try {
parser = context.getResources().getXml(sir.xmlResId);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
// Parse next until start tag is found
}
String nodeName = parser.getName();
if (!NODE_NAME_PREFERENCE_SCREEN.equals(nodeName)) {
throw new RuntimeException(
"XML document must start with <PreferenceScreen> tag; found"
+ nodeName + " at " + parser.getPositionDescription());
}
final int outerDepth = parser.getDepth();
final AttributeSet attrs = Xml.asAttributeSet(parser);
final String screenTitle = XmlParserUtils.getDataTitle(context, attrs);
String key = XmlParserUtils.getDataKey(context, attrs);
String title;
String headerTitle;
String summary;
String headerSummary;
String keywords;
String headerKeywords;
String childFragment;
@DrawableRes
int iconResId;
ResultPayload payload;
boolean enabled;
final String fragmentName = sir.className;
final int rank = sir.rank;
final String intentAction = sir.intentAction;
final String intentTargetPackage = sir.intentTargetPackage;
final String intentTargetClass = sir.intentTargetClass;
Map<String, PreferenceControllerMixin> controllerUriMap = null;
if (fragmentName != null) {
controllerUriMap = DatabaseIndexingUtils
.getPreferenceControllerUriMap(fragmentName, context);
}
// Insert rows for the main PreferenceScreen node. Rewrite the data for removing
// hyphens.
headerTitle = XmlParserUtils.getDataTitle(context, attrs);
headerSummary = XmlParserUtils.getDataSummary(context, attrs);
headerKeywords = XmlParserUtils.getDataKeywords(context, attrs);
enabled = !nonIndexableKeys.contains(key);
// TODO: Set payload type for header results
IndexData.Builder headerBuilder = new IndexData.Builder();
headerBuilder.setLocale(localeStr)
.setEntries(null)
.setClassName(fragmentName)
.setScreenTitle(screenTitle)
.setRank(rank)
.setIntentAction(intentAction)
.setIntentTargetPackage(intentTargetPackage)
.setIntentTargetClass(intentTargetClass)
.setEnabled(enabled)
.setKey(key)
.setUserId(-1 /* default user id */);
// Flag for XML headers which a child element's title.
boolean isHeaderUnique = true;
IndexData.Builder builder;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
nodeName = parser.getName();
title = XmlParserUtils.getDataTitle(context, attrs);
key = XmlParserUtils.getDataKey(context, attrs);
enabled = !nonIndexableKeys.contains(key);
keywords = XmlParserUtils.getDataKeywords(context, attrs);
iconResId = XmlParserUtils.getDataIcon(context, attrs);
if (isHeaderUnique && TextUtils.equals(headerTitle, title)) {
isHeaderUnique = false;
}
builder = new IndexData.Builder();
builder.setLocale(localeStr)
.setClassName(fragmentName)
.setScreenTitle(screenTitle)
.setIconResId(iconResId)
.setRank(rank)
.setIntentAction(intentAction)
.setIntentTargetPackage(intentTargetPackage)
.setIntentTargetClass(intentTargetClass)
.setEnabled(enabled)
.setKey(key)
.setUserId(-1 /* default user id */);
if (!nodeName.equals(NODE_NAME_CHECK_BOX_PREFERENCE)) {
summary = XmlParserUtils.getDataSummary(context, attrs);
String entries = null;
if (nodeName.endsWith(NODE_NAME_LIST_PREFERENCE)) {
entries = XmlParserUtils.getDataEntries(context, attrs);
}
// TODO (b/62254931) index primitives instead of payload
payload = DatabaseIndexingUtils.getPayloadFromUriMap(controllerUriMap, key);
childFragment = XmlParserUtils.getDataChildFragment(context, attrs);
builder.setEntries(entries)
.setChildClassName(childFragment)
.setPayload(payload);
// Insert rows for the child nodes of PreferenceScreen
updateOneRowWithFilteredData(builder, title, summary,
null /* summary off */, keywords);
} else {
String summaryOn = XmlParserUtils.getDataSummaryOn(context, attrs);
String summaryOff = XmlParserUtils.getDataSummaryOff(context, attrs);
if (TextUtils.isEmpty(summaryOn) && TextUtils.isEmpty(summaryOff)) {
summaryOn = XmlParserUtils.getDataSummary(context, attrs);
}
updateOneRowWithFilteredData(builder, title, summaryOn, summaryOff,
keywords);
}
}
// The xml header's title does not match the title of one of the child settings.
if (isHeaderUnique) {
updateOneRowWithFilteredData(headerBuilder, headerTitle, headerSummary,
null /* summary off */, headerKeywords);
}
} catch (XmlPullParserException e) {
throw new RuntimeException("Error parsing PreferenceScreen", e);
} catch (IOException e) {
throw new RuntimeException("Error parsing PreferenceScreen", e);
} finally {
if (parser != null) parser.close();
}
}
@VisibleForTesting
void indexFromProvider(String localeStr, Indexable.SearchIndexProvider provider,
SearchIndexableResource sir, List<String> nonIndexableKeys) {
final String className = sir.className;
final String intentAction = sir.intentAction;
final String intentTargetPackage = sir.intentTargetPackage;
if (provider == null) {
Log.w(LOG_TAG, "Cannot find provider: " + className);
return;
}
final List<SearchIndexableRaw> rawList = provider.getRawDataToIndex(mContext,
true /* enabled */);
if (rawList != null) {
final int rawSize = rawList.size();
for (int i = 0; i < rawSize; i++) {
SearchIndexableRaw raw = rawList.get(i);
// Should be the same locale as the one we are processing
if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
continue;
}
boolean enabled = !nonIndexableKeys.contains(raw.key);
IndexData.Builder builder = new IndexData.Builder();
builder.setLocale(localeStr)
.setEntries(raw.entries)
.setClassName(className)
.setScreenTitle(raw.screenTitle)
.setIconResId(raw.iconResId)
.setIntentAction(raw.intentAction)
.setIntentTargetPackage(raw.intentTargetPackage)
.setIntentTargetClass(raw.intentTargetClass)
.setEnabled(enabled)
.setKey(raw.key)
.setUserId(raw.userId);
updateOneRowWithFilteredData(builder, raw.title, raw.summaryOn, raw.summaryOff,
raw.keywords);
}
}
final List<SearchIndexableResource> resList =
provider.getXmlResourcesToIndex(mContext, true);
if (resList != null) {
final int resSize = resList.size();
for (int i = 0; i < resSize; i++) {
SearchIndexableResource item = resList.get(i);
// Should be the same locale as the one we are processing
if (!item.locale.toString().equalsIgnoreCase(localeStr)) {
continue;
}
item.className = TextUtils.isEmpty(item.className)
? className
: item.className;
item.intentAction = TextUtils.isEmpty(item.intentAction)
? intentAction
: item.intentAction;
item.intentTargetPackage = TextUtils.isEmpty(item.intentTargetPackage)
? intentTargetPackage
: item.intentTargetPackage;
indexFromResource(localeStr, item, nonIndexableKeys);
}
}
}
@VisibleForTesting
void updateOneRowWithFilteredData(IndexData.Builder builder,
String title, String summaryOn, String summaryOff, String keywords) {
final String updatedTitle = DatabaseIndexingUtils.normalizeHyphen(title);
final String updatedSummaryOn = DatabaseIndexingUtils.normalizeHyphen(summaryOn);
final String updatedSummaryOff = DatabaseIndexingUtils.normalizeHyphen(summaryOff);
final String normalizedTitle = DatabaseIndexingUtils.normalizeString(updatedTitle);
final String normalizedSummaryOn = DatabaseIndexingUtils.normalizeString(updatedSummaryOn);
final String normalizedSummaryOff = DatabaseIndexingUtils
.normalizeString(updatedSummaryOff);
final String spaceDelimitedKeywords = DatabaseIndexingUtils.normalizeKeywords(keywords);
builder.setUpdatedTitle(updatedTitle)
.setUpdatedSummaryOn(updatedSummaryOn)
.setUpdatedSummaryOff(updatedSummaryOff)
.setNormalizedTitle(normalizedTitle)
.setNormalizedSummaryOn(normalizedSummaryOn)
.setNormalizedSummaryOff(normalizedSummaryOff)
.setSpaceDelimitedKeywords(spaceDelimitedKeywords);
updateOneRow(builder.build(mContext));
}
private void updateOneRow(IndexData row) {
if (TextUtils.isEmpty(row.updatedTitle)) {
return;
}
ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, row.getDocId());
values.put(LOCALE, row.locale);
values.put(DATA_RANK, row.rank);
values.put(DATA_TITLE, row.updatedTitle);
values.put(DATA_TITLE_NORMALIZED, row.normalizedTitle);
values.put(DATA_SUMMARY_ON, row.updatedSummaryOn);
values.put(DATA_SUMMARY_ON_NORMALIZED, row.normalizedSummaryOn);
values.put(DATA_SUMMARY_OFF, row.updatedSummaryOff);
values.put(DATA_SUMMARY_OFF_NORMALIZED, row.normalizedSummaryOff);
values.put(DATA_ENTRIES, row.entries);
values.put(DATA_KEYWORDS, row.spaceDelimitedKeywords);
values.put(CLASS_NAME, row.className);
values.put(SCREEN_TITLE, row.screenTitle);
values.put(INTENT_ACTION, row.intentAction);
values.put(INTENT_TARGET_PACKAGE, row.intentTargetPackage);
values.put(INTENT_TARGET_CLASS, row.intentTargetClass);
values.put(ICON, row.iconResId);
values.put(ENABLED, row.enabled);
values.put(DATA_KEY_REF, row.key);
values.put(USER_ID, row.userId);
values.put(PAYLOAD_TYPE, row.payloadType);
values.put(PAYLOAD, row.payload);
mDb.replaceOrThrow(TABLE_PREFS_INDEX, null, values);
if (!TextUtils.isEmpty(row.className) && !TextUtils.isEmpty(row.childClassName)) {
ContentValues siteMapPair = new ContentValues();
final int pairDocId = Objects.hash(row.className, row.childClassName);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.DOCID, pairDocId);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.PARENT_CLASS, row.className);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.PARENT_TITLE, row.screenTitle);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.CHILD_CLASS, row.childClassName);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.CHILD_TITLE, row.updatedTitle);
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_SITE_MAP, null, siteMapPair);
}
}
}

View File

@@ -65,7 +65,7 @@ import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INT
/**
* Collects all data from {@link android.provider.SearchIndexablesProvider} to be indexed.
*/
public class IndexableDataCollector {
public class PreIndexDataCollector {
private static final String TAG = "IndexableDataCollector";
@@ -78,7 +78,7 @@ public class IndexableDataCollector {
private PreIndexData mIndexData;
public IndexableDataCollector(Context context) {
public PreIndexDataCollector(Context context) {
mContext = context;
}

View File

@@ -243,7 +243,7 @@ public class CursorToSearchResultConverterTest {
for (SearchResult result : results) {
final InlineSwitchPayload newPayload = (InlineSwitchPayload) result.payload;
final Intent rebuiltIntent = newPayload.getIntent();
assertThat(newPayload.mSettingKey).isEqualTo(uri);
assertThat(newPayload.getKey()).isEqualTo(uri);
assertThat(newPayload.getType()).isEqualTo(type);
assertThat(newPayload.mSettingSource).isEqualTo(source);
assertThat(newPayload.isStandard()).isTrue();

View File

@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyList;
import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.doNothing;
@@ -42,10 +41,8 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.provider.SearchIndexableData;
import android.provider.SearchIndexableResource;
import android.util.ArrayMap;
import com.android.settings.R;
import com.android.settings.TestConfig;
import com.android.settings.search.indexing.PreIndexData;
import com.android.settings.testutils.DatabaseTestUtils;
@@ -65,7 +62,6 @@ import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -149,7 +145,7 @@ public class DatabaseIndexingManagerTest {
Cursor dbCursor = mDb.query("prefs_index", null, null, null, null, null, null);
List<String> columnNames = new ArrayList<>(Arrays.asList(dbCursor.getColumnNames()));
// Note that docid is not included.
List<String> expColumnNames = new ArrayList<>(Arrays.asList(new String[]{
List<String> expColumnNames = Arrays.asList(
"locale",
"data_rank",
"data_title",
@@ -171,187 +167,13 @@ public class DatabaseIndexingManagerTest {
"user_id",
"payload_type",
"payload"
}));
);
// Prevent database schema regressions
assertThat(columnNames).containsAllIn(expColumnNames);
}
// Tests for the flow: IndexOneRaw -> UpdateOneRowWithFilteredData -> UpdateOneRow
@Test
public void testInsertRawColumn_rowInserted() {
SearchIndexableRaw raw = getFakeRaw();
mManager.indexOneSearchIndexableData(mDb, localeStr, raw,
new HashMap<>()/* Non-indexable keys */);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(1);
}
@Test
public void testInsertRawColumn_nonIndexableKey_resultIsDisabled() {
SearchIndexableRaw raw = getFakeRaw();
Map<String, Set<String>> niks = new HashMap<>();
Set<String> keys = new HashSet<>();
keys.add(raw.key);
niks.put(raw.intentTargetPackage, keys);
mManager.indexOneSearchIndexableData(mDb, localeStr, raw, niks);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
assertThat(cursor.getCount()).isEqualTo(1);
}
@Test
public void testInsertRawColumn_rowMatches() {
SearchIndexableRaw raw = getFakeRaw();
mManager.indexOneSearchIndexableData(mDb, localeStr, raw,
new HashMap<>()/* Non-indexable keys */);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
cursor.moveToPosition(0);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(raw.rank);
// Data Title
assertThat(cursor.getString(2)).isEqualTo(updatedTitle);
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo(normalizedTitle);
// Summary On
assertThat(cursor.getString(4)).isEqualTo(updatedSummaryOn);
// Summary On Normalized
assertThat(cursor.getString(5)).isEqualTo(normalizedSummaryOn);
// Summary Off
assertThat(cursor.getString(6)).isEqualTo(updatedSummaryOff);
// Summary off normalized
assertThat(cursor.getString(7)).isEqualTo(normalizedSummaryOff);
// Entries
assertThat(cursor.getString(8)).isEqualTo(raw.entries);
// Keywords
assertThat(cursor.getString(9)).isEqualTo(spaceDelimittedKeywords);
// Screen Title
assertThat(cursor.getString(10)).isEqualTo(raw.screenTitle);
// Class Name
assertThat(cursor.getString(11)).isEqualTo(raw.className);
// Icon
assertThat(cursor.getInt(12)).isEqualTo(raw.iconResId);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(raw.intentAction);
// Target Package
assertThat(cursor.getString(14)).isEqualTo(raw.intentTargetPackage);
// Target Class
assertThat(cursor.getString(15)).isEqualTo(raw.intentTargetClass);
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(raw.enabled);
// Data ref key
assertThat(cursor.getString(17)).isNotNull();
// User Id
assertThat(cursor.getInt(18)).isEqualTo(raw.userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testInsertRawColumn_mismatchedLocale_noRowInserted() {
SearchIndexableRaw raw = getFakeRaw("ca-fr");
mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(0);
}
// Tests for the flow: IndexOneResource -> IndexFromResource ->
// UpdateOneRowWithFilteredData -> UpdateOneRow
@Test
public void testNullResource_NothingInserted() {
mManager.indexOneSearchIndexableData(mDb, localeStr, null /* searchIndexableResource */,
new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(0);
}
@Test
public void testAddResource_RowsInserted() {
SearchIndexableResource resource = getFakeResource(R.xml.display_settings);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(17);
}
@Test
public void testAddResource_withNIKs_rowsInsertedDisabled() {
SearchIndexableResource resource = getFakeResource(R.xml.display_settings);
// Only add 2 of 16 items to be disabled.
String[] keys = {"brightness", "wallpaper"};
Map<String, Set<String>> niks = getNonIndexableKeys(keys);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, niks);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
assertThat(cursor.getCount()).isEqualTo(2);
cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
assertThat(cursor.getCount()).isEqualTo(15);
}
@Test
public void testAddResourceHeader_rowsMatch() {
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(1);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(rank);
// Data Title
assertThat(cursor.getString(2)).isEqualTo("App info");
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo("app info");
// Summary On
assertThat(cursor.getString(4)).isEqualTo("Manage apps, set up quick launch shortcuts");
// Summary On Normalized
assertThat(cursor.getString(5)).isEqualTo("manage apps, set up quick launch shortcuts");
// Summary Off - only on for checkbox preferences
assertThat(cursor.getString(6)).isEmpty();
// Summary off normalized - only on for checkbox preferences
assertThat(cursor.getString(7)).isEmpty();
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isNull();
// Keywords
assertThat(cursor.getString(9)).isEmpty();
// Screen Title
assertThat(cursor.getString(10)).isEqualTo("App info");
// Class Name
assertThat(cursor.getString(11)).isEqualTo(className);
// Icon
assertThat(cursor.getInt(12)).isEqualTo(0);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(action);
// Target Package
assertThat(cursor.getString(14)).isEqualTo(targetPackage);
// Target Class
assertThat(cursor.getString(15)).isEqualTo(targetClass);
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isEqualTo("applications_settings");
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testAddResource_withChildFragment_shouldUpdateSiteMapDb() {
// FIXME: This test was failing. (count = 6 at the end)
@@ -377,376 +199,6 @@ public class DatabaseIndexingManagerTest {
// assertThat(count).isEqualTo(5);
}
@Test
public void testAddResource_customSetting_rowsMatch() {
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
final String prefTitle =
mContext.getString(R.string.fingerprint_swipe_for_notifications_title);
final String prefSummary =
mContext.getString(R.string.fingerprint_swipe_for_notifications_summary);
final String keywords = mContext.getString(R.string.keywords_gesture);
Cursor cursor = mDb.rawQuery(
"SELECT * FROM prefs_index where data_title='" + prefTitle + "'", null);
cursor.moveToFirst();
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(rank);
// Data Title
assertThat(cursor.getString(2)).isEqualTo(prefTitle);
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo(prefTitle.toLowerCase());
// Summary On
assertThat(cursor.getString(4)).isEqualTo(prefSummary);
// Summary On Normalized
assertThat(cursor.getString(5)).isEqualTo(prefSummary.toLowerCase());
// Summary Off - only on for checkbox preferences
assertThat(cursor.getString(6)).isEmpty();
// Summary off normalized - only on for checkbox preferences
assertThat(cursor.getString(7)).isEmpty();
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isNull();
// Keywords
assertThat(cursor.getString(9)).isEqualTo(keywords);
// Screen Title
assertThat(cursor.getString(10)).isEqualTo(
mContext.getString(R.string.fingerprint_swipe_for_notifications_title));
// Class Name
assertThat(cursor.getString(11)).isEqualTo(className);
// Icon
assertThat(cursor.getInt(12)).isEqualTo(noIcon);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(action);
// Target Package
assertThat(cursor.getString(14)).isEqualTo(targetPackage);
// Target Class
assertThat(cursor.getString(15)).isEqualTo(targetClass);
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isEqualTo("gesture_swipe_down_fingerprint");
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testAddResource_checkboxPreference_rowsMatch() {
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
/* Should return 6 results, with the following titles:
* Advanced Settings, Apps, Manage Apps, Preferred install location, Running Services
*/
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(0);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(rank);
// Data Title
assertThat(cursor.getString(2)).isEqualTo("Advanced settings");
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo("advanced settings");
// Summary On
assertThat(cursor.getString(4)).isEqualTo("Enable more settings options");
// Summary On Normalized
assertThat(cursor.getString(5)).isEqualTo("enable more settings options");
// Summary Off
assertThat(cursor.getString(6)).isEqualTo("Enable more settings options");
// Summary Off
assertThat(cursor.getString(7)).isEqualTo("enable more settings options");
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isNull();
// Keywords
assertThat(cursor.getString(9)).isEmpty();
// Screen Title
assertThat(cursor.getString(10)).isEqualTo("App info");
// Class Name
assertThat(cursor.getString(11)).isEqualTo(className);
// Icon
assertThat(cursor.getInt(12)).isEqualTo(noIcon);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(action);
// Target Package
assertThat(cursor.getString(14)).isEqualTo(targetPackage);
// Target Class
assertThat(cursor.getString(15)).isEqualTo(targetClass);
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isEqualTo("toggle_advanced_settings");
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testAddResource_listPreference_rowsMatch() {
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(3);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(rank);
// Data Title
assertThat(cursor.getString(2)).isEqualTo("Preferred install location");
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo("preferred install location");
// Summary On
assertThat(cursor.getString(4)).isEqualTo(
"Change the preferred installation location for new apps");
// Summary On Normalized
assertThat(cursor.getString(5)).isEqualTo(
"change the preferred installation location for new apps");
// Summary Off - only on for checkbox preferences
assertThat(cursor.getString(6)).isEmpty();
// Summary off normalized - only on for checkbox preferences
assertThat(cursor.getString(7)).isEmpty();
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isEqualTo("Internal device storage|Removable SD card|" +
"Let the system decide|");
// Keywords
assertThat(cursor.getString(9)).isEmpty();
// Screen Title
assertThat(cursor.getString(10)).isEqualTo("App info");
// Class Name
assertThat(cursor.getString(11)).isEqualTo(className);
// Icon
assertThat(cursor.getInt(12)).isEqualTo(noIcon);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(action);
// Target Package
assertThat(cursor.getString(14)).isEqualTo(targetPackage);
// Target Class
assertThat(cursor.getString(15)).isEqualTo(targetClass);
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isEqualTo("app_install_location");
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testAddResource_iconAddedFromXml() {
SearchIndexableResource resource = getFakeResource(R.xml.connected_devices);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(0);
// Icon
assertThat(cursor.getInt(12)).isNotEqualTo(noIcon);
}
// Tests for the flow: IndexOneResource -> IndexFromProvider -> IndexFromResource ->
// UpdateOneRowWithFilteredData -> UpdateOneRow
@Test
public void testResourceProvider_rowInserted() {
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
resource.xmlResId = 0;
resource.className = "com.android.settings.display.ScreenZoomSettings";
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(1);
}
@Test
public void testResourceProvider_rowMatches() {
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
resource.xmlResId = 0;
resource.className = "com.android.settings.display.ScreenZoomSettings";
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
cursor.moveToPosition(0);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(0);
// Data Title
assertThat(cursor.getString(2)).isEqualTo("Display size");
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo("display size");
// Summary On
assertThat(cursor.getString(4)).isEmpty();
// Summary On Normalized
assertThat(cursor.getString(5)).isEmpty();
// Summary Off - only on for checkbox preferences
assertThat(cursor.getString(6)).isEmpty();
// Summary off normalized - only on for checkbox preferences
assertThat(cursor.getString(7)).isEmpty();
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isNull();
// Keywords
assertThat(cursor.getString(9)).isEqualTo("display density screen zoom scale scaling");
// Screen Title
assertThat(cursor.getString(10)).isEqualTo("Display size");
// Class Name
assertThat(cursor.getString(11))
.isEqualTo("com.android.settings.display.ScreenZoomSettings");
// Icon
assertThat(cursor.getInt(12)).isEqualTo(noIcon);
// Intent Action
assertThat(cursor.getString(13)).isNull();
// Target Package
assertThat(cursor.getString(14)).isNull();
// Target Class
assertThat(cursor.getString(15)).isNull();
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isNull();
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testResourceProvider_resourceRowInserted() {
SearchIndexableResource resource = getFakeResource(0);
resource.className = "com.android.settings.LegalSettings";
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(6);
}
@Test
public void testResourceProvider_resourceRowMatches() {
SearchIndexableResource resource = getFakeResource(0 /* xml */);
resource.className = "com.android.settings.display.ScreenZoomSettings";
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(0);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(0);
// Data Title
assertThat(cursor.getString(2)).isEqualTo("Display size");
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo("display size");
// Summary On
assertThat(cursor.getString(4)).isEmpty();
// Summary On Normalized
assertThat(cursor.getString(5)).isEmpty();
// Summary Off - only on for checkbox preferences
assertThat(cursor.getString(6)).isEmpty();
// Summary off normalized - only on for checkbox preferences
assertThat(cursor.getString(7)).isEmpty();
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isNull();
// Keywords
assertThat(cursor.getString(9)).isEqualTo(
"display density screen zoom scale scaling");
// Screen Title
assertThat(cursor.getString(10)).isEqualTo("Display size");
// Class Name
assertThat(cursor.getString(11))
.isEqualTo("com.android.settings.display.ScreenZoomSettings");
// Icon
assertThat(cursor.getInt(12)).isEqualTo(noIcon);
// Intent Action
assertThat(cursor.getString(13)).isNull();
// Target Package
assertThat(cursor.getString(14)).isNull();
// Target Class
assertThat(cursor.getString(15)).isNull();
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isNull();
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testResourceProvider_disabledResource_rowsInserted() {
SearchIndexableResource resource = getFakeResource(0 /* xml */);
resource.className = "com.android.settings.LegalSettings";
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
new HashMap<String, Set<String>>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
assertThat(cursor.getCount()).isEqualTo(1);
cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
assertThat(cursor.getCount()).isEqualTo(5);
}
@Test
public void testResource_withTitleAndSettingName_titleNotInserted() {
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
mManager.indexFromResource(mDb, localeStr, resource, new ArrayList<String>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE" +
" enabled = 1", null);
assertThat(cursor.getCount()).isEqualTo(1);
}
@Test
public void testResourceProvider_nonSubsettingIntent() {
SearchIndexableResource resource = getFakeResource(0 /* xml */);
String fakeAction = "fake_action";
resource.className = "com.android.settings.LegalSettings";
resource.intentAction = fakeAction;
resource.intentTargetPackage = SearchIndexableResources.SUBSETTING_TARGET_PACKAGE;
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
cursor.moveToPosition(0);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(fakeAction);
// Target Package
assertThat(cursor.getString(14))
.isEqualTo(SearchIndexableResources.SUBSETTING_TARGET_PACKAGE);
}
// Test new public indexing flow
@Test
@@ -824,14 +276,6 @@ public class DatabaseIndexingManagerTest {
assertThat(IndexDatabaseHelper.isBuildIndexed(mContext, Build.FINGERPRINT)).isTrue();
}
@Test
public void testFullUpdatedDatabase_noData_addDataToDatabaseNotCalled() {
PreIndexData emptydata = new PreIndexData();
mManager.updateDatabase(emptydata, true /* isFullIndex */, localeStr);
verify(mManager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(),
anyList(), anyMap());
}
@Test
public void testLocaleUpdated_afterIndexing_localeNotAdded() {
PreIndexData emptydata = new PreIndexData();
@@ -1013,28 +457,6 @@ public class DatabaseIndexingManagerTest {
return data;
}
private SearchIndexableResource getFakeResource(int xml) {
SearchIndexableResource sir = new SearchIndexableResource(mContext);
sir.rank = rank;
sir.xmlResId = xml;
sir.className = className;
sir.packageName = packageName;
sir.iconResId = iconResId;
sir.intentAction = action;
sir.intentTargetPackage = targetPackage;
sir.intentTargetClass = targetClass;
sir.enabled = enabled;
return sir;
}
private Map<String, Set<String>> getNonIndexableKeys(String[] keys) {
Map<String, Set<String>> niks = new HashMap<>();
Set<String> keysList = new HashSet<>();
keysList.addAll(Arrays.asList(keys));
niks.put(packageName, keysList);
return niks;
}
private void insertSpecialCase(String specialCase, boolean enabled, String key) {
ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, specialCase.hashCode());

View File

@@ -40,7 +40,7 @@ public class InlineListPayloadTest {
intent, true /* isAvailable */, 1 /* numOptions */, 1 /* default */);
final Intent retainedIntent = payload.getIntent();
assertThat(payload.mSettingKey).isEqualTo(uri);
assertThat(payload.getKey()).isEqualTo(uri);
assertThat(payload.getType()).isEqualTo(type);
assertThat(payload.mSettingSource).isEqualTo(source);
assertThat(payload.getAvailability()).isEqualTo(ResultPayload.Availability.AVAILABLE);
@@ -69,7 +69,7 @@ public class InlineListPayloadTest {
.CREATOR.createFromParcel(parcel);
final Intent builtIntent = payload.getIntent();
assertThat(payload.mSettingKey).isEqualTo(uri);
assertThat(payload.getKey()).isEqualTo(uri);
assertThat(payload.getType()).isEqualTo(type);
assertThat(payload.mSettingSource).isEqualTo(source);
assertThat(payload.getAvailability()).isEqualTo(ResultPayload.Availability.AVAILABLE);

View File

@@ -64,7 +64,7 @@ public class InlineSwitchPayloadTest {
InlineSwitchPayload payload = new InlineSwitchPayload(uri, source, 1, intent, true,
1 /* default */);
final Intent retainedIntent = payload.getIntent();
assertThat(payload.mSettingKey).isEqualTo(uri);
assertThat(payload.getKey()).isEqualTo(uri);
assertThat(payload.getType()).isEqualTo(type);
assertThat(payload.mSettingSource).isEqualTo(source);
assertThat(payload.isStandard()).isTrue();
@@ -93,7 +93,7 @@ public class InlineSwitchPayloadTest {
InlineSwitchPayload payload = InlineSwitchPayload.CREATOR.createFromParcel(parcel);
final Intent builtIntent = payload.getIntent();
assertThat(payload.mSettingKey).isEqualTo(uri);
assertThat(payload.getKey()).isEqualTo(uri);
assertThat(payload.getType()).isEqualTo(type);
assertThat(payload.mSettingSource).isEqualTo(source);
assertThat(payload.isStandard()).isTrue();

View File

@@ -0,0 +1,689 @@
/*
* 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.indexing;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.provider.SearchIndexableResource;
import com.android.settings.TestConfig;
import com.android.settings.search.IndexDatabaseHelper;
import com.android.settings.search.ResultPayload;
import com.android.settings.search.ResultPayloadUtils;
import com.android.settings.search.SearchIndexableRaw;
import com.android.settings.search.SearchIndexableResources;
import com.android.settings.testutils.DatabaseTestUtils;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
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.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import static com.android.settings.R.*;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class IndexDataConverterTest {
private final String localeStr = "en_US";
private final int rank = 8;
private final String title = "title\u2011title";
private final String updatedTitle = "title-title";
private final String normalizedTitle = "titletitle";
private final String summaryOn = "summary\u2011on";
private final String updatedSummaryOn = "summary-on";
private final String normalizedSummaryOn = "summaryon";
private final String summaryOff = "summary\u2011off";
private final String updatedSummaryOff = "summary-off";
private final String normalizedSummaryOff = "summaryoff";
private final String entries = "entries";
private final String keywords = "keywords, keywordss, keywordsss";
private final String spaceDelimittedKeywords = "keywords keywordss keywordsss";
private final String screenTitle = "screen title";
private final String className = "class name";
private final int iconResId = 0xff;
private final int noIcon = 0;
private final String action = "action";
private final String targetPackage = "target package";
private final String targetClass = "target class";
private final String packageName = "package name";
private final String key = "key";
private final int userId = -1;
private final boolean enabled = true;
private Context mContext;
private IndexDataConverter mConverter;
private SQLiteDatabase mDb;
@Before
public void setUp() {
mContext = spy(RuntimeEnvironment.application);
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
mConverter = spy(new IndexDataConverter(mContext, mDb));
}
@After
public void cleanUp() {
DatabaseTestUtils.clearDb(mContext);
}
@Test
public void testInsertRawColumn_rowInserted() {
SearchIndexableRaw raw = getFakeRaw();
mConverter.indexOneSearchIndexableData(localeStr, raw,
new HashMap<>()/* Non-indexable keys */);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(1);
}
@Test
public void testInsertRawColumn_nonIndexableKey_resultIsDisabled() {
SearchIndexableRaw raw = getFakeRaw();
Map<String, Set<String>> niks = new HashMap<>();
Set<String> keys = new HashSet<>();
keys.add(raw.key);
niks.put(raw.intentTargetPackage, keys);
mConverter.indexOneSearchIndexableData(localeStr, raw, niks);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
assertThat(cursor.getCount()).isEqualTo(1);
}
@Test
public void testInsertRawColumn_rowMatches() {
SearchIndexableRaw raw = getFakeRaw();
mConverter.indexOneSearchIndexableData(localeStr, raw,
new HashMap<>()/* Non-indexable keys */);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
cursor.moveToPosition(0);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(raw.rank);
// Data Title
assertThat(cursor.getString(2)).isEqualTo(updatedTitle);
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo(normalizedTitle);
// Summary On
assertThat(cursor.getString(4)).isEqualTo(updatedSummaryOn);
// Summary On Normalized
assertThat(cursor.getString(5)).isEqualTo(normalizedSummaryOn);
// Summary Off
assertThat(cursor.getString(6)).isEqualTo(updatedSummaryOff);
// Summary off normalized
assertThat(cursor.getString(7)).isEqualTo(normalizedSummaryOff);
// Entries
assertThat(cursor.getString(8)).isEqualTo(raw.entries);
// Keywords
assertThat(cursor.getString(9)).isEqualTo(spaceDelimittedKeywords);
// Screen Title
assertThat(cursor.getString(10)).isEqualTo(raw.screenTitle);
// Class Name
assertThat(cursor.getString(11)).isEqualTo(raw.className);
// Icon
assertThat(cursor.getInt(12)).isEqualTo(raw.iconResId);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(raw.intentAction);
// Target Package
assertThat(cursor.getString(14)).isEqualTo(raw.intentTargetPackage);
// Target Class
assertThat(cursor.getString(15)).isEqualTo(raw.intentTargetClass);
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(raw.enabled);
// Data ref key
assertThat(cursor.getString(17)).isNotNull();
// User Id
assertThat(cursor.getInt(18)).isEqualTo(raw.userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testInsertRawColumn_mismatchedLocale_noRowInserted() {
SearchIndexableRaw raw = getFakeRaw("ca-fr");
mConverter.indexOneSearchIndexableData(localeStr, raw, null /* Non-indexable keys */);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(0);
}
// Tests for the flow: IndexOneResource -> IndexFromResource ->
// UpdateOneRowWithFilteredData -> UpdateOneRow
@Test
public void testNullResource_NothingInserted() {
mConverter.indexOneSearchIndexableData(localeStr, null /* searchIndexableResource */,
new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(0);
}
@Test
public void testAddResource_RowsInserted() {
SearchIndexableResource resource = getFakeResource(xml.display_settings);
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(17);
}
@Test
public void testAddResource_withNIKs_rowsInsertedDisabled() {
SearchIndexableResource resource = getFakeResource(xml.display_settings);
// Only add 2 of 16 items to be disabled.
String[] keys = {"brightness", "wallpaper"};
Map<String, Set<String>> niks = getNonIndexableKeys(keys);
mConverter.indexOneSearchIndexableData(localeStr, resource, niks);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
assertThat(cursor.getCount()).isEqualTo(2);
cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
assertThat(cursor.getCount()).isEqualTo(15);
}
@Test
public void testAddResourceHeader_rowsMatch() {
SearchIndexableResource resource = getFakeResource(xml.application_settings);
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(1);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(rank);
// Data Title
assertThat(cursor.getString(2)).isEqualTo("App info");
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo("app info");
// Summary On
assertThat(cursor.getString(4)).isEqualTo("Manage apps, set up quick launch shortcuts");
// Summary On Normalized
assertThat(cursor.getString(5)).isEqualTo("manage apps, set up quick launch shortcuts");
// Summary Off - only on for checkbox preferences
assertThat(cursor.getString(6)).isEmpty();
// Summary off normalized - only on for checkbox preferences
assertThat(cursor.getString(7)).isEmpty();
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isNull();
// Keywords
assertThat(cursor.getString(9)).isEmpty();
// Screen Title
assertThat(cursor.getString(10)).isEqualTo("App info");
// Class Name
assertThat(cursor.getString(11)).isEqualTo(className);
// Icon
assertThat(cursor.getInt(12)).isEqualTo(0);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(action);
// Target Package
assertThat(cursor.getString(14)).isEqualTo(targetPackage);
// Target Class
assertThat(cursor.getString(15)).isEqualTo(targetClass);
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isEqualTo("applications_settings");
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testAddResource_customSetting_rowsMatch() {
SearchIndexableResource resource = getFakeResource(xml.swipe_to_notification_settings);
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
final String prefTitle =
mContext.getString(string.fingerprint_swipe_for_notifications_title);
final String prefSummary =
mContext.getString(string.fingerprint_swipe_for_notifications_summary);
final String keywords = mContext.getString(string.keywords_gesture);
Cursor cursor = mDb.rawQuery(
"SELECT * FROM prefs_index where data_title='" + prefTitle + "'", null);
cursor.moveToFirst();
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(rank);
// Data Title
assertThat(cursor.getString(2)).isEqualTo(prefTitle);
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo(prefTitle.toLowerCase());
// Summary On
assertThat(cursor.getString(4)).isEqualTo(prefSummary);
// Summary On Normalized
assertThat(cursor.getString(5)).isEqualTo(prefSummary.toLowerCase());
// Summary Off - only on for checkbox preferences
assertThat(cursor.getString(6)).isEmpty();
// Summary off normalized - only on for checkbox preferences
assertThat(cursor.getString(7)).isEmpty();
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isNull();
// Keywords
assertThat(cursor.getString(9)).isEqualTo(keywords);
// Screen Title
assertThat(cursor.getString(10)).isEqualTo(
mContext.getString(string.fingerprint_swipe_for_notifications_title));
// Class Name
assertThat(cursor.getString(11)).isEqualTo(className);
// Icon
assertThat(cursor.getInt(12)).isEqualTo(noIcon);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(action);
// Target Package
assertThat(cursor.getString(14)).isEqualTo(targetPackage);
// Target Class
assertThat(cursor.getString(15)).isEqualTo(targetClass);
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isEqualTo("gesture_swipe_down_fingerprint");
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testAddResource_checkboxPreference_rowsMatch() {
SearchIndexableResource resource = getFakeResource(xml.application_settings);
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
/* Should return 6 results, with the following titles:
* Advanced Settings, Apps, Manage Apps, Preferred install location, Running Services
*/
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(0);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(rank);
// Data Title
assertThat(cursor.getString(2)).isEqualTo("Advanced settings");
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo("advanced settings");
// Summary On
assertThat(cursor.getString(4)).isEqualTo("Enable more settings options");
// Summary On Normalized
assertThat(cursor.getString(5)).isEqualTo("enable more settings options");
// Summary Off
assertThat(cursor.getString(6)).isEqualTo("Enable more settings options");
// Summary Off
assertThat(cursor.getString(7)).isEqualTo("enable more settings options");
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isNull();
// Keywords
assertThat(cursor.getString(9)).isEmpty();
// Screen Title
assertThat(cursor.getString(10)).isEqualTo("App info");
// Class Name
assertThat(cursor.getString(11)).isEqualTo(className);
// Icon
assertThat(cursor.getInt(12)).isEqualTo(noIcon);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(action);
// Target Package
assertThat(cursor.getString(14)).isEqualTo(targetPackage);
// Target Class
assertThat(cursor.getString(15)).isEqualTo(targetClass);
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isEqualTo("toggle_advanced_settings");
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testAddResource_listPreference_rowsMatch() {
SearchIndexableResource resource = getFakeResource(xml.application_settings);
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(3);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(rank);
// Data Title
assertThat(cursor.getString(2)).isEqualTo("Preferred install location");
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo("preferred install location");
// Summary On
assertThat(cursor.getString(4)).isEqualTo(
"Change the preferred installation location for new apps");
// Summary On Normalized
assertThat(cursor.getString(5)).isEqualTo(
"change the preferred installation location for new apps");
// Summary Off - only on for checkbox preferences
assertThat(cursor.getString(6)).isEmpty();
// Summary off normalized - only on for checkbox preferences
assertThat(cursor.getString(7)).isEmpty();
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isEqualTo("Internal device storage|Removable SD card|" +
"Let the system decide|");
// Keywords
assertThat(cursor.getString(9)).isEmpty();
// Screen Title
assertThat(cursor.getString(10)).isEqualTo("App info");
// Class Name
assertThat(cursor.getString(11)).isEqualTo(className);
// Icon
assertThat(cursor.getInt(12)).isEqualTo(noIcon);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(action);
// Target Package
assertThat(cursor.getString(14)).isEqualTo(targetPackage);
// Target Class
assertThat(cursor.getString(15)).isEqualTo(targetClass);
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isEqualTo("app_install_location");
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testAddResource_iconAddedFromXml() {
SearchIndexableResource resource = getFakeResource(xml.connected_devices);
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(0);
// Icon
assertThat(cursor.getInt(12)).isNotEqualTo(noIcon);
}
// Tests for the flow: IndexOneResource -> IndexFromProvider -> IndexFromResource ->
// UpdateOneRowWithFilteredData -> UpdateOneRow
@Test
public void testResourceProvider_rowInserted() {
SearchIndexableResource resource = getFakeResource(xml.swipe_to_notification_settings);
resource.xmlResId = 0;
resource.className = "com.android.settings.display.ScreenZoomSettings";
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(1);
}
@Test
public void testResourceProvider_rowMatches() {
SearchIndexableResource resource = getFakeResource(xml.swipe_to_notification_settings);
resource.xmlResId = 0;
resource.className = "com.android.settings.display.ScreenZoomSettings";
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
cursor.moveToPosition(0);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(0);
// Data Title
assertThat(cursor.getString(2)).isEqualTo("Display size");
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo("display size");
// Summary On
assertThat(cursor.getString(4)).isEmpty();
// Summary On Normalized
assertThat(cursor.getString(5)).isEmpty();
// Summary Off - only on for checkbox preferences
assertThat(cursor.getString(6)).isEmpty();
// Summary off normalized - only on for checkbox preferences
assertThat(cursor.getString(7)).isEmpty();
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isNull();
// Keywords
assertThat(cursor.getString(9)).isEqualTo("display density screen zoom scale scaling");
// Screen Title
assertThat(cursor.getString(10)).isEqualTo("Display size");
// Class Name
assertThat(cursor.getString(11))
.isEqualTo("com.android.settings.display.ScreenZoomSettings");
// Icon
assertThat(cursor.getInt(12)).isEqualTo(noIcon);
// Intent Action
assertThat(cursor.getString(13)).isNull();
// Target Package
assertThat(cursor.getString(14)).isNull();
// Target Class
assertThat(cursor.getString(15)).isNull();
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isNull();
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testResourceProvider_resourceRowInserted() {
SearchIndexableResource resource = getFakeResource(0);
resource.className = "com.android.settings.LegalSettings";
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(6);
}
@Test
public void testResourceProvider_resourceRowMatches() {
SearchIndexableResource resource = getFakeResource(0 /* xml */);
resource.className = "com.android.settings.display.ScreenZoomSettings";
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(0);
// Locale
assertThat(cursor.getString(0)).isEqualTo(localeStr);
// Data Rank
assertThat(cursor.getInt(1)).isEqualTo(0);
// Data Title
assertThat(cursor.getString(2)).isEqualTo("Display size");
// Normalized Title
assertThat(cursor.getString(3)).isEqualTo("display size");
// Summary On
assertThat(cursor.getString(4)).isEmpty();
// Summary On Normalized
assertThat(cursor.getString(5)).isEmpty();
// Summary Off - only on for checkbox preferences
assertThat(cursor.getString(6)).isEmpty();
// Summary off normalized - only on for checkbox preferences
assertThat(cursor.getString(7)).isEmpty();
// Entries - only on for list preferences
assertThat(cursor.getString(8)).isNull();
// Keywords
assertThat(cursor.getString(9)).isEqualTo(
"display density screen zoom scale scaling");
// Screen Title
assertThat(cursor.getString(10)).isEqualTo("Display size");
// Class Name
assertThat(cursor.getString(11))
.isEqualTo("com.android.settings.display.ScreenZoomSettings");
// Icon
assertThat(cursor.getInt(12)).isEqualTo(noIcon);
// Intent Action
assertThat(cursor.getString(13)).isNull();
// Target Package
assertThat(cursor.getString(14)).isNull();
// Target Class
assertThat(cursor.getString(15)).isNull();
// Enabled
assertThat(cursor.getInt(16) == 1).isEqualTo(enabled);
// Data ref key
assertThat(cursor.getString(17)).isNull();
// User Id
assertThat(cursor.getInt(18)).isEqualTo(userId);
// Payload Type - default is 0
assertThat(cursor.getInt(19)).isEqualTo(0);
// Payload - should be updated to real payloads as controllers are added
byte[] payload = cursor.getBlob(20);
ResultPayload unmarshalledPayload = ResultPayloadUtils.unmarshall(payload,
ResultPayload.CREATOR);
assertThat(unmarshalledPayload).isInstanceOf(ResultPayload.class);
}
@Test
public void testResourceProvider_disabledResource_rowsInserted() {
SearchIndexableResource resource = getFakeResource(0 /* xml */);
resource.className = "com.android.settings.LegalSettings";
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
assertThat(cursor.getCount()).isEqualTo(1);
cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
assertThat(cursor.getCount()).isEqualTo(5);
}
@Test
public void testResource_withTitleAndSettingName_titleNotInserted() {
SearchIndexableResource resource = getFakeResource(xml.swipe_to_notification_settings);
mConverter.indexFromResource(localeStr, resource, new ArrayList<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE" +
" enabled = 1", null);
assertThat(cursor.getCount()).isEqualTo(1);
}
@Test
public void testResourceProvider_nonSubsettingIntent() {
SearchIndexableResource resource = getFakeResource(0 /* xml */);
String fakeAction = "fake_action";
resource.className = "com.android.settings.LegalSettings";
resource.intentAction = fakeAction;
resource.intentTargetPackage = SearchIndexableResources.SUBSETTING_TARGET_PACKAGE;
mConverter.indexOneSearchIndexableData(localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
cursor.moveToPosition(0);
// Intent Action
assertThat(cursor.getString(13)).isEqualTo(fakeAction);
// Target Package
assertThat(cursor.getString(14))
.isEqualTo(SearchIndexableResources.SUBSETTING_TARGET_PACKAGE);
}
private SearchIndexableRaw getFakeRaw() {
return getFakeRaw(localeStr);
}
private SearchIndexableRaw getFakeRaw(String localeStr) {
SearchIndexableRaw data = new SearchIndexableRaw(mContext);
data.locale = new Locale(localeStr);
data.rank = rank;
data.title = title;
data.summaryOn = summaryOn;
data.summaryOff = summaryOff;
data.entries = entries;
data.keywords = keywords;
data.screenTitle = screenTitle;
data.className = className;
data.packageName = packageName;
data.iconResId = iconResId;
data.intentAction = action;
data.intentTargetPackage = targetPackage;
data.intentTargetClass = targetClass;
data.key = key;
data.userId = userId;
data.enabled = enabled;
return data;
}
private SearchIndexableResource getFakeResource(int xml) {
SearchIndexableResource sir = new SearchIndexableResource(mContext);
sir.rank = rank;
sir.xmlResId = xml;
sir.className = className;
sir.packageName = packageName;
sir.iconResId = iconResId;
sir.intentAction = action;
sir.intentTargetPackage = targetPackage;
sir.intentTargetClass = targetClass;
sir.enabled = enabled;
return sir;
}
private Map<String, Set<String>> getNonIndexableKeys(String[] keys) {
Map<String, Set<String>> niks = new HashMap<>();
Set<String> keysList = new HashSet<>();
keysList.addAll(Arrays.asList(keys));
niks.put(packageName, keysList);
return niks;
}
}

View File

@@ -15,16 +15,17 @@
*
*/
package com.android.settings.search;
package com.android.settings.search.indexing;
import android.content.ComponentName;
import android.content.Intent;
import android.content.Context;
import com.android.settings.search.InlineSwitchPayload;
import com.android.settings.search.ResultPayload;
import com.android.settings.search.ResultPayloadUtils;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search.DatabaseIndexingManager.DatabaseRow;
import com.android.settings.search.DatabaseIndexingManager.DatabaseRow.Builder;
import org.junit.Before;
import org.junit.Test;
@@ -36,8 +37,8 @@ import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class DatabaseRowTest {
private Builder mBuilder;
public class IndexDataTest {
private IndexData.Builder mBuilder;
private static final String LOCALE = "locale";
private static final String UPDATED_TITLE = "updated title";
@@ -69,13 +70,13 @@ public class DatabaseRowTest {
@Test
public void testFullRowBuild_nonNull() {
DatabaseRow row = generateRow();
IndexData row = generateRow();
assertThat(row).isNotNull();
}
@Test
public void testPrimitivesBuild_noDataLoss() {
DatabaseRow row = generateRow();
IndexData row = generateRow();
assertThat(row.locale).isEqualTo(LOCALE);
assertThat(row.updatedTitle).isEqualTo(UPDATED_TITLE);
@@ -102,7 +103,7 @@ public class DatabaseRowTest {
@Test
public void testGenericIntent_addedToPayload() {
final DatabaseRow row = generateRow();
final IndexData row = generateRow();
final ResultPayload payload = ResultPayloadUtils.unmarshall(row.payload,
ResultPayload.CREATOR);
final ComponentName name = payload.getIntent().getComponent();
@@ -116,12 +117,12 @@ public class DatabaseRowTest {
final InlineSwitchPayload payload = new InlineSwitchPayload(URI, 0 /* mSettingSource */,
1 /* onValue */, null /* intent */, true /* isDeviceSupported */, 1 /* default */);
mBuilder.setPayload(payload);
final DatabaseRow row = generateRow();
final IndexData row = generateRow();
final InlineSwitchPayload unmarshalledPayload = ResultPayloadUtils
.unmarshall(row.payload, InlineSwitchPayload.CREATOR);
assertThat(row.payloadType).isEqualTo(ResultPayload.PayloadType.INLINE_SWITCH);
assertThat(unmarshalledPayload.mSettingKey).isEqualTo(URI);
assertThat(unmarshalledPayload.getKey()).isEqualTo(URI);
}
@Test
@@ -135,7 +136,7 @@ public class DatabaseRowTest {
final InlineSwitchPayload payload = new InlineSwitchPayload(URI, 0 /* mSettingSource */,
1 /* onValue */, intent, true /* isDeviceSupported */, 1 /* default */);
mBuilder.setPayload(payload);
final DatabaseRow row = generateRow();
final IndexData row = generateRow();
final InlineSwitchPayload unmarshalledPayload = ResultPayloadUtils
.unmarshall(row.payload, InlineSwitchPayload.CREATOR);
final ComponentName name = unmarshalledPayload.getIntent().getComponent();
@@ -145,12 +146,12 @@ public class DatabaseRowTest {
}
private DatabaseRow generateRow() {
private IndexData generateRow() {
return mBuilder.build(mContext);
}
private DatabaseRow.Builder createBuilder() {
mBuilder = new DatabaseRow.Builder();
private IndexData.Builder createBuilder() {
mBuilder = new IndexData.Builder();
mBuilder.setLocale(LOCALE)
.setUpdatedTitle(UPDATED_TITLE)
.setNormalizedTitle(NORMALIZED_TITLE)

View File

@@ -47,7 +47,7 @@ import static org.mockito.Mockito.spy;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class IndexableDataCollectorTest {
public class PreIndexDataCollectorTest {
private final String AUTHORITY_ONE = "authority";
private final String PACKAGE_ONE = "com.android.settings";
@@ -57,7 +57,7 @@ public class IndexableDataCollectorTest {
Context mContext;
IndexableDataCollector mDataCollector;
PreIndexDataCollector mDataCollector;
@Before
public void setUp() {
@@ -66,7 +66,7 @@ public class IndexableDataCollectorTest {
doReturn(mResolver).when(mContext).getContentResolver();
//doReturn(mPackageManager).when(mContext).getPackageManager();
mDataCollector = spy(new IndexableDataCollector(mContext));
mDataCollector = spy(new PreIndexDataCollector(mContext));
}
@Test