Merge "Move search indexing into a separate class"
This commit is contained in:
committed by
Android (Google) Code Review
commit
e4cab00ffa
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
287
src/com/android/settings/search/indexing/IndexData.java
Normal file
287
src/com/android/settings/search/indexing/IndexData.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
510
src/com/android/settings/search/indexing/IndexDataConverter.java
Normal file
510
src/com/android/settings/search/indexing/IndexDataConverter.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user