Add dynamic Preferences indexing (part 2)

- change the Index SQL model. Add a new "enabled" column.
- use that column for issuing a more restrictive search query
- change the SearchIndexProvider API to pass the "enable" state
- apply it to Bluetooth settings
- refactor the list of indexable resources (SearchIndexableResources)

Change-Id: Ic900fb27cb12a285a80d953aa1aa88f0070cd986
This commit is contained in:
Fabrice Di Meglio
2014-03-20 19:52:29 -07:00
parent 30eb2d3dd1
commit 51bfee595c
9 changed files with 641 additions and 464 deletions

View File

@@ -67,12 +67,13 @@ public class WallpaperTypeSettings extends SettingsPreferenceFragment implements
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new SearchIndexProvider() { new SearchIndexProvider() {
@Override @Override
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context) { public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
return null; return null;
} }
@Override @Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context) { public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>(); final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
final Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER); final Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER);

View File

@@ -28,6 +28,7 @@ import android.widget.Toast;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.WirelessSettings; import com.android.settings.WirelessSettings;
import com.android.settings.search.Index;
/** /**
* BluetoothEnabler is a helper to manage the Bluetooth on/off checkbox * BluetoothEnabler is a helper to manage the Bluetooth on/off checkbox
@@ -132,6 +133,7 @@ public final class BluetoothEnabler implements CompoundButton.OnCheckedChangeLis
case BluetoothAdapter.STATE_ON: case BluetoothAdapter.STATE_ON:
setChecked(true); setChecked(true);
mSwitch.setEnabled(true); mSwitch.setEnabled(true);
updateSearchIndex(true);
break; break;
case BluetoothAdapter.STATE_TURNING_OFF: case BluetoothAdapter.STATE_TURNING_OFF:
mSwitch.setEnabled(false); mSwitch.setEnabled(false);
@@ -139,10 +141,12 @@ public final class BluetoothEnabler implements CompoundButton.OnCheckedChangeLis
case BluetoothAdapter.STATE_OFF: case BluetoothAdapter.STATE_OFF:
setChecked(false); setChecked(false);
mSwitch.setEnabled(true); mSwitch.setEnabled(true);
updateSearchIndex(false);
break; break;
default: default:
setChecked(false); setChecked(false);
mSwitch.setEnabled(true); mSwitch.setEnabled(true);
updateSearchIndex(false);
} }
} }
@@ -159,4 +163,9 @@ public final class BluetoothEnabler implements CompoundButton.OnCheckedChangeLis
} }
} }
} }
private void updateSearchIndex(boolean isBluetoothOn) {
Index.getInstance(mContext).updateFromClassNameResource(
BluetoothSettings.class.getName(), isBluetoothOn);
}
} }

View File

@@ -48,7 +48,6 @@ import com.android.settings.search.Indexable;
import com.android.settings.search.SearchIndexableRaw; import com.android.settings.search.SearchIndexableRaw;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -424,12 +423,13 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem
new SearchIndexProvider() { new SearchIndexProvider() {
@Override @Override
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context) { public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
return null; return null;
} }
@Override @Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context) { public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>(); final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
@@ -450,6 +450,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem
data = new SearchIndexableRaw(context); data = new SearchIndexableRaw(context);
data.title = device.getName(); data.title = device.getName();
data.screenTitle = res.getString(R.string.bluetooth_settings); data.screenTitle = res.getString(R.string.bluetooth_settings);
data.enabled = enabled;
result.add(data); result.add(data);
} }

View File

@@ -65,6 +65,7 @@ public class Index {
public static final int COLUMN_INDEX_INTENT_ACTION = 7; public static final int COLUMN_INDEX_INTENT_ACTION = 7;
public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8; public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9; public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
public static final int COLUMN_INDEX_ENABLED = 10;
// If you change the order of columns here, you SHOULD change the COLUMN_INDEX_XXX values // If you change the order of columns here, you SHOULD change the COLUMN_INDEX_XXX values
private static final String[] SELECT_COLUMNS = new String[] { private static final String[] SELECT_COLUMNS = new String[] {
@@ -93,13 +94,30 @@ public class Index {
private static final String HYPHEN = "-"; private static final String HYPHEN = "-";
private static Index sInstance; private static Index sInstance;
private final AtomicBoolean mIsAvailable = new AtomicBoolean(false); private final AtomicBoolean mIsAvailable = new AtomicBoolean(false);
private final UpdateData mDataToProcess = new UpdateData();
private final UpdateData mUpdateData = new UpdateData();
private final Context mContext; private final Context mContext;
/**
* A private class to describe the update data for the Index database
*/
private class UpdateData {
public List<SearchIndexableData> dataToUpdate;
public List<String> dataToDelete;
public boolean forceUpdate = false;
public UpdateData() {
dataToUpdate = new ArrayList<SearchIndexableData>();
dataToDelete = new ArrayList<String>();
}
public void clear() {
dataToUpdate.clear();
dataToDelete.clear();
forceUpdate = false;
}
}
/** /**
* A basic singleton * A basic singleton
*/ */
@@ -118,26 +136,32 @@ public class Index {
return mIsAvailable.get(); return mIsAvailable.get();
} }
public Cursor search(String query) {
final String sql = buildSQL(query);
Log.d(LOG_TAG, "Query: " + sql);
return getReadableDatabase().rawQuery(sql, null);
}
public void addIndexableData(SearchIndexableData data) { public void addIndexableData(SearchIndexableData data) {
synchronized (mUpdateData) { synchronized (mDataToProcess) {
mUpdateData.dataToAdd.add(data); mDataToProcess.dataToUpdate.add(data);
} }
} }
public void addIndexableData(SearchIndexableResource[] array) { public void addIndexableData(SearchIndexableResource[] array) {
synchronized (mUpdateData) { synchronized (mDataToProcess) {
final int count = array.length; final int count = array.length;
for (int n = 0; n < count; n++) { for (int n = 0; n < count; n++) {
mUpdateData.dataToAdd.add(array[n]); mDataToProcess.dataToUpdate.add(array[n]);
} }
} }
} }
public void deleteIndexableData(String[] array) { public void deleteIndexableData(String[] array) {
synchronized (mUpdateData) { synchronized (mDataToProcess) {
final int count = array.length; final int count = array.length;
for (int n = 0; n < count; n++) { for (int n = 0; n < count; n++) {
mUpdateData.dataToDelete.add(array[n]); mDataToProcess.dataToDelete.add(array[n]);
} }
} }
} }
@@ -152,6 +176,50 @@ public class Index {
final ResolveInfo info = list.get(n); final ResolveInfo info = list.get(n);
final String authority = info.providerInfo.authority; final String authority = info.providerInfo.authority;
final String packageName = info.providerInfo.packageName; final String packageName = info.providerInfo.packageName;
addIndexablesFromRemoteProvider(packageName, authority);
}
return updateInternal();
}
public boolean updateFromRemoteProvider(String packageName, String authority) {
if (!addIndexablesFromRemoteProvider(packageName, authority)) {
return false;
}
return updateInternal();
}
public boolean updateFromClassNameResource(String className, boolean includeInSearchResults) {
if (className == null) {
throw new IllegalArgumentException("class name cannot be null!");
}
final SearchIndexableResource res = SearchIndexableResources.getResourceByName(className);
if (res == null ) {
Log.e(LOG_TAG, "Cannot find SearchIndexableResources for class name: " + className);
return false;
}
res.enabled = includeInSearchResults;
addIndexableData(res);
mDataToProcess.forceUpdate = true;
boolean result = updateInternal();
res.enabled = false;
return result;
}
private boolean updateFromSearchIndexableData(SearchIndexableData data) {
addIndexableData(data);
return updateInternal();
}
private SQLiteDatabase getReadableDatabase() {
return IndexDatabaseHelper.getInstance(mContext).getReadableDatabase();
}
private SQLiteDatabase getWritableDatabase() {
return IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
}
private boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
final Context packageContext; final Context packageContext;
try { try {
packageContext = mContext.createPackageContext(packageName, 0); packageContext = mContext.createPackageContext(packageName, 0);
@@ -163,16 +231,14 @@ public class Index {
final Uri uriForRawData = buildUriForRawData(authority); final Uri uriForRawData = buildUriForRawData(authority);
addIndexablesForRawDataUri(packageContext, packageName, uriForRawData, addIndexablesForRawDataUri(packageContext, packageName, uriForRawData,
SearchIndexablesContract.INDEXABLES_RAW_COLUMNS); SearchIndexablesContract.INDEXABLES_RAW_COLUMNS);
return true;
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
Log.w(LOG_TAG, "Could not create context for " + packageName + ": " Log.w(LOG_TAG, "Could not create context for " + packageName + ": "
+ Log.getStackTraceString(e)); + Log.getStackTraceString(e));
continue; return false;
} }
} }
return updateInternal();
}
private static Uri buildUriForXmlResources(String authority) { private static Uri buildUriForXmlResources(String authority) {
return Uri.parse("content://" + authority + "/" + return Uri.parse("content://" + authority + "/" +
SearchIndexablesContract.INDEXABLES_XML_RES_PATH); SearchIndexablesContract.INDEXABLES_XML_RES_PATH);
@@ -183,10 +249,28 @@ public class Index {
SearchIndexablesContract.INDEXABLES_RAW_PATH); SearchIndexablesContract.INDEXABLES_RAW_PATH);
} }
private void addIndexablesForXmlResourceUri(Context packageContext, String packageName, Uri uri, private boolean updateInternal() {
String[] projection) { synchronized (mDataToProcess) {
final ContentResolver resolver = packageContext.getContentResolver(); final UpdateIndexTask task = new UpdateIndexTask();
task.execute(mDataToProcess);
try {
final boolean result = task.get();
mDataToProcess.clear();
return result;
} catch (InterruptedException e) {
Log.e(LOG_TAG, "Cannot update index: " + e.getMessage());
return false;
} catch (ExecutionException e) {
Log.e(LOG_TAG, "Cannot update index: " + e.getMessage());
return false;
}
}
}
private void addIndexablesForXmlResourceUri(Context packageContext, String packageName,
Uri uri, String[] projection) {
final ContentResolver resolver = packageContext.getContentResolver();
final Cursor cursor = resolver.query(uri, projection, final Cursor cursor = resolver.query(uri, projection,
null, null, null); null, null, null);
@@ -227,10 +311,10 @@ public class Index {
} }
} }
private void addIndexablesForRawDataUri(Context packageContext, String packageName, Uri uri, private void addIndexablesForRawDataUri(Context packageContext, String packageName,
String[] projection) { Uri uri, String[] projection) {
final ContentResolver resolver = packageContext.getContentResolver();
final ContentResolver resolver = packageContext.getContentResolver();
final Cursor cursor = resolver.query(uri, projection, final Cursor cursor = resolver.query(uri, projection,
null, null, null); null, null, null);
@@ -278,30 +362,6 @@ public class Index {
} }
} }
private boolean updateInternal() {
synchronized (mUpdateData) {
final UpdateIndexTask task = new UpdateIndexTask();
task.execute(mUpdateData);
try {
final boolean result = task.get();
mUpdateData.clear();
return result;
} catch (InterruptedException e) {
Log.e(LOG_TAG, "Cannot update index: " + e.getMessage());
return false;
} catch (ExecutionException e) {
Log.e(LOG_TAG, "Cannot update index: " + e.getMessage());
return false;
}
}
}
public Cursor search(String query) {
final String sql = buildSQL(query);
Log.d(LOG_TAG, "Query: " + sql);
return getReadableDatabase().rawQuery(sql, null);
}
private String buildSQL(String query) { private String buildSQL(String query) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(buildSQLForColumn(query, MATCH_COLUMNS)); sb.append(buildSQLForColumn(query, MATCH_COLUMNS));
@@ -335,6 +395,9 @@ public class Index {
sb.append(IndexColumns.LOCALE); sb.append(IndexColumns.LOCALE);
sb.append(" = "); sb.append(" = ");
DatabaseUtils.appendEscapedSQLString(sb, Locale.getDefault().toString()); DatabaseUtils.appendEscapedSQLString(sb, Locale.getDefault().toString());
sb.append(" AND ");
sb.append(IndexColumns.ENABLED);
sb.append(" = 1");
return sb.toString(); return sb.toString();
} }
@@ -353,105 +416,8 @@ public class Index {
return sb.toString(); return sb.toString();
} }
private SQLiteDatabase getReadableDatabase() { private void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
return IndexDatabaseHelper.getInstance(mContext).getReadableDatabase(); SearchIndexableData data) {
}
private SQLiteDatabase getWritableDatabase() {
return IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
}
/**
* A private class to describe the update data for the Index database
*/
private class UpdateData {
public List<SearchIndexableData> dataToAdd;
public List<String> dataToDelete;
public UpdateData() {
dataToAdd = new ArrayList<SearchIndexableData>();
dataToDelete = new ArrayList<String>();
}
public void clear() {
dataToAdd.clear();
dataToDelete.clear();
}
}
/**
* A private class for updating the Index database
*/
private class UpdateIndexTask extends AsyncTask<UpdateData, Integer, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
mIsAvailable.set(false);
}
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
mIsAvailable.set(true);
}
@Override
protected Boolean doInBackground(UpdateData... params) {
boolean result = false;
final List<SearchIndexableData> dataToAdd = params[0].dataToAdd;
final List<String> dataToDelete = params[0].dataToDelete;
final SQLiteDatabase database = getWritableDatabase();
final String localeStr = Locale.getDefault().toString();
try {
database.beginTransaction();
if (dataToAdd.size() > 0) {
processDataToAdd(database, localeStr, dataToAdd);
}
if (dataToDelete.size() > 0) {
processDataToDelete(database, localeStr, dataToDelete);
}
database.setTransactionSuccessful();
result = true;
} finally {
database.endTransaction();
}
return result;
}
private boolean processDataToDelete(SQLiteDatabase database, String localeStr,
List<String> dataToDelete) {
boolean result = false;
final long current = System.currentTimeMillis();
final int count = dataToDelete.size();
for (int n = 0; n < count; n++) {
final String data = dataToDelete.get(n);
delete(database, data);
}
final long now = System.currentTimeMillis();
Log.d(LOG_TAG, "Deleting data for locale '" + localeStr + "' took " +
(now - current) + " millis");
return result;
}
private boolean processDataToAdd(SQLiteDatabase database, String localeStr,
List<SearchIndexableData> dataToAdd) {
if (isLocaleAlreadyIndexed(database, localeStr)) {
Log.d(LOG_TAG, "Locale '" + localeStr + "' is already indexed");
return true;
}
boolean result = false;
final long current = System.currentTimeMillis();
final int count = dataToAdd.size();
for (int n = 0; n < count; n++) {
final SearchIndexableData data = dataToAdd.get(n);
if (data instanceof SearchIndexableResource) { if (data instanceof SearchIndexableResource) {
indexOneResource(database, localeStr, (SearchIndexableResource) data); indexOneResource(database, localeStr, (SearchIndexableResource) data);
} else if (data instanceof SearchIndexableRaw) { } else if (data instanceof SearchIndexableRaw) {
@@ -459,12 +425,6 @@ public class Index {
} }
} }
final long now = System.currentTimeMillis();
Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
(now - current) + " millis");
return result;
}
private void indexOneResource(SQLiteDatabase database, String localeStr, private void indexOneResource(SQLiteDatabase database, String localeStr,
SearchIndexableResource sir) { SearchIndexableResource sir) {
if (sir.xmlResId > 0) { if (sir.xmlResId > 0) {
@@ -477,111 +437,6 @@ public class Index {
} }
} }
private void indexFromLocalProvider(SQLiteDatabase database, String localeStr,
SearchIndexableResource sir) {
try {
final Class<?> clazz = Class.forName(sir.className);
if (Indexable.class.isAssignableFrom(clazz)) {
final Field f = clazz.getField("SEARCH_INDEX_DATA_PROVIDER");
final Indexable.SearchIndexProvider provider =
(Indexable.SearchIndexProvider) f.get(null);
final List<SearchIndexableRaw> rawList =
provider.getRawDataToIndex(sir.context);
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;
}
insertOneRowWithFilteredData(database, localeStr,
raw.title,
raw.summary,
sir.className,
raw.screenTitle,
sir.iconResId,
sir.rank,
raw.keywords,
raw.intentAction,
raw.intentTargetPackage,
raw.intentTargetClass
);
}
}
final List<SearchIndexableResource> resList =
provider.getXmlResourcesToIndex(sir.context);
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;
}
indexFromResource(sir.context, database, localeStr,
item.xmlResId, item.className, item.iconResId, item.rank,
item.intentAction, item.intentTargetPackage,
item.intentTargetClass);
}
}
}
} catch (ClassNotFoundException e) {
Log.e(LOG_TAG, "Cannot find class: " + sir.className, e);
} catch (NoSuchFieldException e) {
Log.e(LOG_TAG, "Cannot find field 'SEARCH_INDEX_DATA_PROVIDER'", e);
} catch (IllegalAccessException e) {
Log.e(LOG_TAG, "Illegal access to field 'SEARCH_INDEX_DATA_PROVIDER'", e);
}
}
private void indexOneRaw(SQLiteDatabase database, String localeStr,
SearchIndexableRaw raw) {
// Should be the same locale as the one we are processing
if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
return;
}
insertOneRowWithFilteredData(database, localeStr,
raw.title,
raw.summary,
raw.className,
raw.screenTitle,
raw.iconResId,
raw.rank,
raw.keywords,
raw.intentAction,
raw.intentTargetPackage,
raw.intentTargetClass
);
}
private boolean isLocaleAlreadyIndexed(SQLiteDatabase database, String locale) {
Cursor cursor = null;
boolean result = false;
final StringBuilder sb = new StringBuilder(IndexColumns.LOCALE);
sb.append(" = ");
DatabaseUtils.appendEscapedSQLString(sb, locale);
try {
// We care only for 1 row
cursor = database.query(Tables.TABLE_PREFS_INDEX, null,
sb.toString(), null, null, null, null, "1");
final int count = cursor.getCount();
result = (count >= 1);
} finally {
if (cursor != null) {
cursor.close();
}
}
return result;
}
private void indexFromResource(Context context, SQLiteDatabase database, String localeStr, private void indexFromResource(Context context, SQLiteDatabase database, String localeStr,
int xmlResId, String fragmentName, int iconResId, int rank, int xmlResId, String fragmentName, int iconResId, int rank,
String intentAction, String intentTargetPackage, String intentTargetClass) { String intentAction, String intentTargetPackage, String intentTargetClass) {
@@ -613,9 +468,9 @@ public class Index {
// Insert rows for the main PreferenceScreen node. Rewrite the data for removing // Insert rows for the main PreferenceScreen node. Rewrite the data for removing
// hyphens. // hyphens.
insertOneRowWithFilteredData(database, localeStr, title, summary, fragmentName, updateOneRowWithFilteredData(database, localeStr, title, summary, fragmentName,
screenTitle, iconResId, rank, keywords, screenTitle, iconResId, rank, keywords,
intentAction, intentTargetPackage, intentTargetClass); intentAction, intentTargetPackage, intentTargetClass, true);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -628,9 +483,9 @@ public class Index {
keywords = getDataKeywords(context, attrs); keywords = getDataKeywords(context, attrs);
// Insert rows for the child nodes of PreferenceScreen // Insert rows for the child nodes of PreferenceScreen
insertOneRowWithFilteredData(database, localeStr, title, summary, fragmentName, updateOneRowWithFilteredData(database, localeStr, title, summary, fragmentName,
screenTitle, iconResId, rank, keywords, screenTitle, iconResId, rank, keywords,
intentAction, intentTargetPackage, intentTargetClass); intentAction, intentTargetPackage, intentTargetClass, true);
} }
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
@@ -642,10 +497,96 @@ public class Index {
} }
} }
private void insertOneRowWithFilteredData(SQLiteDatabase database, String locale, private void indexOneRaw(SQLiteDatabase database, String localeStr,
SearchIndexableRaw raw) {
// Should be the same locale as the one we are processing
if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
return;
}
updateOneRowWithFilteredData(database, localeStr,
raw.title,
raw.summary,
raw.className,
raw.screenTitle,
raw.iconResId,
raw.rank,
raw.keywords,
raw.intentAction,
raw.intentTargetPackage,
raw.intentTargetClass,
raw.enabled);
}
private void indexFromLocalProvider(SQLiteDatabase database, String localeStr,
SearchIndexableResource sir) {
try {
final Class<?> clazz = Class.forName(sir.className);
if (Indexable.class.isAssignableFrom(clazz)) {
final Field f = clazz.getField("SEARCH_INDEX_DATA_PROVIDER");
final Indexable.SearchIndexProvider provider =
(Indexable.SearchIndexProvider) f.get(null);
final List<SearchIndexableRaw> rawList =
provider.getRawDataToIndex(sir.context, sir.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;
}
updateOneRowWithFilteredData(database, localeStr,
raw.title,
raw.summary,
sir.className,
raw.screenTitle,
sir.iconResId,
sir.rank,
raw.keywords,
raw.intentAction,
raw.intentTargetPackage,
raw.intentTargetClass,
raw.enabled);
}
}
final List<SearchIndexableResource> resList =
provider.getXmlResourcesToIndex(sir.context, sir.enabled);
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;
}
indexFromResource(sir.context, database, localeStr,
item.xmlResId, item.className, item.iconResId, item.rank,
item.intentAction, item.intentTargetPackage,
item.intentTargetClass);
}
}
}
} catch (ClassNotFoundException e) {
Log.e(LOG_TAG, "Cannot find class: " + sir.className, e);
} catch (NoSuchFieldException e) {
Log.e(LOG_TAG, "Cannot find field 'SEARCH_INDEX_DATA_PROVIDER'", e);
} catch (IllegalAccessException e) {
Log.e(LOG_TAG, "Illegal access to field 'SEARCH_INDEX_DATA_PROVIDER'", e);
}
}
private void updateOneRowWithFilteredData(SQLiteDatabase database, String locale,
String title, String summary, String className, String screenTitle, String title, String summary, String className, String screenTitle,
int iconResId, int rank, String keywords, int iconResId, int rank, String keywords,
String intentAction, String intentTargetPackage, String intentTargetClass) { String intentAction, String intentTargetPackage, String intentTargetClass,
boolean enabled) {
String updatedTitle; String updatedTitle;
if (title != null) { if (title != null) {
@@ -665,23 +606,26 @@ public class Index {
String normalizedTitle = updatedTitle.replaceAll(HYPHEN, EMPTY); String normalizedTitle = updatedTitle.replaceAll(HYPHEN, EMPTY);
String normalizedSummary = updatedSummary.replaceAll(HYPHEN, EMPTY); String normalizedSummary = updatedSummary.replaceAll(HYPHEN, EMPTY);
insertOneRow(database, locale, updateOneRow(database, locale,
updatedTitle, normalizedTitle, updatedSummary, normalizedSummary, updatedTitle, normalizedTitle, updatedSummary, normalizedSummary,
className, screenTitle, iconResId, rank, keywords, className, screenTitle, iconResId, rank, keywords,
intentAction, intentTargetPackage, intentTargetClass); intentAction, intentTargetPackage, intentTargetClass, enabled);
} }
private void insertOneRow(SQLiteDatabase database, String locale, private void updateOneRow(SQLiteDatabase database, String locale,
String updatedTitle, String normalizedTitle, String updatedTitle, String normalizedTitle,
String updatedSummary, String normalizedSummary, String updatedSummary, String normalizedSummary,
String className, String screenTitle, String className, String screenTitle,
int iconResId, int rank, String keywords, int iconResId, int rank, String keywords,
String intentAction, String intentTargetPackage, String intentTargetClass) { String intentAction, String intentTargetPackage, String intentTargetClass,
boolean enabled) {
if (TextUtils.isEmpty(updatedTitle)) { if (TextUtils.isEmpty(updatedTitle)) {
return; return;
} }
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(IndexColumns.DOCID, updatedTitle.hashCode());
values.put(IndexColumns.LOCALE, locale); values.put(IndexColumns.LOCALE, locale);
values.put(IndexColumns.DATA_RANK, rank); values.put(IndexColumns.DATA_RANK, rank);
values.put(IndexColumns.DATA_TITLE, updatedTitle); values.put(IndexColumns.DATA_TITLE, updatedTitle);
@@ -695,15 +639,9 @@ public class Index {
values.put(IndexColumns.INTENT_TARGET_PACKAGE, intentTargetPackage); values.put(IndexColumns.INTENT_TARGET_PACKAGE, intentTargetPackage);
values.put(IndexColumns.INTENT_TARGET_CLASS, intentTargetClass); values.put(IndexColumns.INTENT_TARGET_CLASS, intentTargetClass);
values.put(IndexColumns.ICON, iconResId); values.put(IndexColumns.ICON, iconResId);
values.put(IndexColumns.ENABLED, enabled);
database.insertOrThrow(Tables.TABLE_PREFS_INDEX, null, values); database.replaceOrThrow(Tables.TABLE_PREFS_INDEX, null, values);
}
private int delete(SQLiteDatabase database, String title) {
final String whereClause = IndexColumns.DATA_TITLE + "=?";
final String[] whereArgs = new String[] { title };
return database.delete(Tables.TABLE_PREFS_INDEX, whereClause, whereArgs);
} }
private String getDataTitle(Context context, AttributeSet attrs) { private String getDataTitle(Context context, AttributeSet attrs) {
@@ -719,9 +657,7 @@ public class Index {
} }
private String getDataKeywords(Context context, AttributeSet attrs) { private String getDataKeywords(Context context, AttributeSet attrs) {
return getData(context, attrs, return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_keywords);
R.styleable.Preference,
R.styleable.Preference_keywords);
} }
private String getData(Context context, AttributeSet set, int[] attrs, int resId) { private String getData(Context context, AttributeSet set, int[] attrs, int resId) {
@@ -738,5 +674,116 @@ public class Index {
} }
return (data != null) ? data.toString() : null; return (data != null) ? data.toString() : null;
} }
/**
* A private class for updating the Index database
*/
private class UpdateIndexTask extends AsyncTask<UpdateData, Integer, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
mIsAvailable.set(false);
}
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
mIsAvailable.set(true);
}
@Override
protected Boolean doInBackground(UpdateData... params) {
boolean result = false;
final List<SearchIndexableData> dataToUpdate = params[0].dataToUpdate;
final List<String> dataToDelete = params[0].dataToDelete;
final boolean forceUpdate = params[0].forceUpdate;
final SQLiteDatabase database = getWritableDatabase();
final String localeStr = Locale.getDefault().toString();
try {
database.beginTransaction();
if (dataToUpdate.size() > 0) {
processDataToUpdate(database, localeStr, dataToUpdate, forceUpdate);
}
if (dataToDelete.size() > 0) {
processDataToDelete(database, localeStr, dataToDelete);
}
database.setTransactionSuccessful();
result = true;
} finally {
database.endTransaction();
}
return result;
}
private boolean processDataToUpdate(SQLiteDatabase database, String localeStr,
List<SearchIndexableData> dataToUpdate, boolean forceUpdate) {
if (!forceUpdate && isLocaleAlreadyIndexed(database, localeStr)) {
Log.d(LOG_TAG, "Locale '" + localeStr + "' is already indexed");
return true;
}
boolean result = false;
final long current = System.currentTimeMillis();
final int count = dataToUpdate.size();
for (int n = 0; n < count; n++) {
final SearchIndexableData data = dataToUpdate.get(n);
indexOneSearchIndexableData(database, localeStr, data);
}
final long now = System.currentTimeMillis();
Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
(now - current) + " millis");
return result;
}
private boolean processDataToDelete(SQLiteDatabase database, String localeStr,
List<String> dataToDelete) {
boolean result = false;
final long current = System.currentTimeMillis();
final int count = dataToDelete.size();
for (int n = 0; n < count; n++) {
final String data = dataToDelete.get(n);
delete(database, data);
}
final long now = System.currentTimeMillis();
Log.d(LOG_TAG, "Deleting data for locale '" + localeStr + "' took " +
(now - current) + " millis");
return result;
}
private int delete(SQLiteDatabase database, String title) {
final String whereClause = IndexColumns.DATA_TITLE + "=?";
final String[] whereArgs = new String[] { title };
return database.delete(Tables.TABLE_PREFS_INDEX, whereClause, whereArgs);
}
private boolean isLocaleAlreadyIndexed(SQLiteDatabase database, String locale) {
Cursor cursor = null;
boolean result = false;
final StringBuilder sb = new StringBuilder(IndexColumns.LOCALE);
sb.append(" = ");
DatabaseUtils.appendEscapedSQLString(sb, locale);
try {
// We care only for 1 row
cursor = database.query(Tables.TABLE_PREFS_INDEX, null,
sb.toString(), null, null, null, null, "1");
final int count = cursor.getCount();
result = (count >= 1);
} finally {
if (cursor != null) {
cursor.close();
}
}
return result;
}
} }
} }

View File

@@ -28,7 +28,7 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "IndexDatabaseHelper"; private static final String TAG = "IndexDatabaseHelper";
private static final String DATABASE_NAME = "search_index.db"; private static final String DATABASE_NAME = "search_index.db";
private static final int DATABASE_VERSION = 102; private static final int DATABASE_VERSION = 103;
public interface Tables { public interface Tables {
public static final String TABLE_PREFS_INDEX = "prefs_index"; public static final String TABLE_PREFS_INDEX = "prefs_index";
@@ -36,6 +36,7 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
} }
public interface IndexColumns { public interface IndexColumns {
public static final String DOCID = "docid";
public static final String LOCALE = "locale"; public static final String LOCALE = "locale";
public static final String DATA_RANK = "data_rank"; public static final String DATA_RANK = "data_rank";
public static final String DATA_TITLE = "data_title"; public static final String DATA_TITLE = "data_title";
@@ -49,6 +50,7 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
public static final String INTENT_TARGET_PACKAGE = "intent_target_package"; public static final String INTENT_TARGET_PACKAGE = "intent_target_package";
public static final String INTENT_TARGET_CLASS = "intent_target_class"; public static final String INTENT_TARGET_CLASS = "intent_target_class";
public static final String ICON = "icon"; public static final String ICON = "icon";
public static final String ENABLED = "enabled";
} }
public interface MetaColumns { public interface MetaColumns {
@@ -83,6 +85,8 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
IndexColumns.INTENT_TARGET_PACKAGE + IndexColumns.INTENT_TARGET_PACKAGE +
", " + ", " +
IndexColumns.INTENT_TARGET_CLASS + IndexColumns.INTENT_TARGET_CLASS +
", " +
IndexColumns.ENABLED +
");"; ");";
private static final String CREATE_META_TABLE = private static final String CREATE_META_TABLE =
@@ -125,9 +129,9 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
@Override @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 100 || oldVersion == 101) { if (oldVersion == 100 || oldVersion == 101 || oldVersion == 102) {
Log.w(TAG, "Detected schema version 100 or 101. " + Log.w(TAG, "Detected schema version 100, 101 or 102. " +
"Index needs to be rebuilt for schema version 102"); "Index needs to be rebuilt for schema version 103");
// We need to drop the tables and recreate them // We need to drop the tables and recreate them
dropTables(db); dropTables(db);
bootstrapDB(db); bootstrapDB(db);

View File

@@ -39,18 +39,23 @@ public interface Indexable {
* *
* See {@link android.provider.SearchIndexableResource} * See {@link android.provider.SearchIndexableResource}
* *
* @param context the context *
* @param context the context.
* @param enabled hint telling if the data needs to be considered into the search results
* or not.
* @return a list of {@link android.provider.SearchIndexableResource} references. * @return a list of {@link android.provider.SearchIndexableResource} references.
* Can be null. * Can be null.
*/ */
List<SearchIndexableResource> getXmlResourcesToIndex(Context context); List<SearchIndexableResource> getXmlResourcesToIndex(Context context, boolean enabled);
/** /**
* Return a list of raw data for indexing. See {@link SearchIndexableRaw} * Return a list of raw data for indexing. See {@link SearchIndexableRaw}
* *
* @param context the context * @param context the context.
* @param enabled hint telling if the data needs to be considered into the search results
* or not.
* @return a list of {@link SearchIndexableRaw} references. Can be null. * @return a list of {@link SearchIndexableRaw} references. Can be null.
*/ */
List<SearchIndexableRaw> getRawDataToIndex(Context context); List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled);
} }
} }

View File

@@ -0,0 +1,172 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.search;
import android.provider.SearchIndexableResource;
import com.android.settings.DateTimeSettings;
import com.android.settings.DevelopmentSettings;
import com.android.settings.DeviceInfoSettings;
import com.android.settings.DisplaySettings;
import com.android.settings.HomeSettings;
import com.android.settings.PrivacySettings;
import com.android.settings.R;
import com.android.settings.SecuritySettings;
import com.android.settings.SoundSettings;
import com.android.settings.WallpaperTypeSettings;
import com.android.settings.WirelessSettings;
import com.android.settings.accessibility.AccessibilitySettings;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.deviceinfo.Memory;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
import com.android.settings.location.LocationSettings;
import com.android.settings.net.DataUsageMeteredSettings;
import com.android.settings.print.PrintSettingsFragment;
import com.android.settings.users.UserSettings;
import com.android.settings.wifi.WifiSettings;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
public final class SearchIndexableResources {
private static int NO_DATA_RES_ID = 0;
private static HashMap<String, SearchIndexableResource> sResMap =
new HashMap<String, SearchIndexableResource>();
static {
sResMap.put(WifiSettings.class.getName(),
new SearchIndexableResource(1,
NO_DATA_RES_ID,
WifiSettings.class.getName(),
R.drawable.ic_settings_wireless));
sResMap.put(BluetoothSettings.class.getName(),
new SearchIndexableResource(2,
NO_DATA_RES_ID,
BluetoothSettings.class.getName(),
R.drawable.ic_settings_bluetooth2));
sResMap.put(DataUsageMeteredSettings.class.getName(),
new SearchIndexableResource(3, R.xml.data_usage_metered_prefs,
DataUsageMeteredSettings.class.getName(),
R.drawable.ic_settings_data_usage));
sResMap.put(WirelessSettings.class.getName(),
new SearchIndexableResource(4, R.xml.wireless_settings,
WirelessSettings.class.getName(),
R.drawable.empty_icon));
sResMap.put(HomeSettings.class.getName(),
new SearchIndexableResource(5, R.xml.home_selection,
HomeSettings.class.getName(),
R.drawable.ic_settings_home));
sResMap.put(SoundSettings.class.getName(),
new SearchIndexableResource(6, R.xml.sound_settings,
SoundSettings.class.getName(),
R.drawable.ic_settings_sound));
sResMap.put(DisplaySettings.class.getName(),
new SearchIndexableResource(7, R.xml.display_settings,
DisplaySettings.class.getName(),
R.drawable.ic_settings_display));
sResMap.put(WallpaperTypeSettings.class.getName(),
new SearchIndexableResource(7, NO_DATA_RES_ID,
WallpaperTypeSettings.class.getName(),
R.drawable.ic_settings_display));
sResMap.put(Memory.class.getName(),
new SearchIndexableResource(8, R.xml.device_info_memory,
Memory.class.getName(),
R.drawable.ic_settings_storage));
sResMap.put(PowerUsageSummary.class.getName(),
new SearchIndexableResource(9, R.xml.power_usage_summary,
PowerUsageSummary.class.getName(),
R.drawable.ic_settings_battery));
sResMap.put(UserSettings.class.getName(),
new SearchIndexableResource(10, R.xml.user_settings,
UserSettings.class.getName(),
R.drawable.ic_settings_multiuser));
sResMap.put(LocationSettings.class.getName(),
new SearchIndexableResource(11, R.xml.location_settings,
LocationSettings.class.getName(),
R.drawable.ic_settings_location));
sResMap.put(SecuritySettings.class.getName(),
new SearchIndexableResource(12, R.xml.security_settings,
SecuritySettings.class.getName(),
R.drawable.ic_settings_security));
sResMap.put(InputMethodAndLanguageSettings.class.getName(),
new SearchIndexableResource(13, R.xml.language_settings,
InputMethodAndLanguageSettings.class.getName(),
R.drawable.ic_settings_language));
sResMap.put(PrivacySettings.class.getName(),
new SearchIndexableResource(14, R.xml.privacy_settings,
PrivacySettings.class.getName(),
R.drawable.ic_settings_backup));
sResMap.put(DateTimeSettings.class.getName(),
new SearchIndexableResource(15, R.xml.date_time_prefs,
DateTimeSettings.class.getName(),
R.drawable.ic_settings_date_time));
sResMap.put(AccessibilitySettings.class.getName(),
new SearchIndexableResource(16, R.xml.accessibility_settings,
AccessibilitySettings.class.getName(),
R.drawable.ic_settings_accessibility));
sResMap.put(PrintSettingsFragment.class.getName(),
new SearchIndexableResource(17, R.xml.print_settings,
PrintSettingsFragment.class.getName(),
com.android.internal.R.drawable.ic_print));
sResMap.put(DevelopmentSettings.class.getName(),
new SearchIndexableResource(18, R.xml.development_prefs,
DevelopmentSettings.class.getName(),
R.drawable.ic_settings_development));
sResMap.put(DeviceInfoSettings.class.getName(),
new SearchIndexableResource(19, R.xml.device_info_settings,
DeviceInfoSettings.class.getName(),
R.drawable.ic_settings_about));
}
private SearchIndexableResources() {
}
public static int size() {
return sResMap.size();
}
public static SearchIndexableResource getResourceByName(String className) {
return sResMap.get(className);
}
public static Collection<SearchIndexableResource> values() {
return sResMap.values();
}
}

View File

@@ -42,77 +42,14 @@ import com.android.settings.print.PrintSettingsFragment;
import com.android.settings.users.UserSettings; import com.android.settings.users.UserSettings;
import com.android.settings.wifi.WifiSettings; import com.android.settings.wifi.WifiSettings;
import java.util.Collection;
import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS; import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS; import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider { public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
private static final String TAG = "SettingsSearchIndexablesProvider"; private static final String TAG = "SettingsSearchIndexablesProvider";
private static int NO_DATA_RES_ID = 0;
private static SearchIndexableResource[] INDEXABLE_REFS = new SearchIndexableResource[] {
new SearchIndexableResource(1, NO_DATA_RES_ID,
WifiSettings.class.getName(),
R.drawable.ic_settings_wireless),
new SearchIndexableResource(2, NO_DATA_RES_ID,
BluetoothSettings.class.getName(),
R.drawable.ic_settings_bluetooth2),
new SearchIndexableResource(3, R.xml.data_usage_metered_prefs,
DataUsageMeteredSettings.class.getName(),
R.drawable.ic_settings_data_usage),
new SearchIndexableResource(4, R.xml.wireless_settings,
WirelessSettings.class.getName(),
R.drawable.empty_icon),
new SearchIndexableResource(5, R.xml.home_selection,
HomeSettings.class.getName(),
R.drawable.ic_settings_home),
new SearchIndexableResource(6, R.xml.sound_settings,
SoundSettings.class.getName(),
R.drawable.ic_settings_sound),
new SearchIndexableResource(7, R.xml.display_settings,
DisplaySettings.class.getName(),
R.drawable.ic_settings_display),
new SearchIndexableResource(7, NO_DATA_RES_ID,
WallpaperTypeSettings.class.getName(),
R.drawable.ic_settings_display),
new SearchIndexableResource(8, R.xml.device_info_memory,
Memory.class.getName(),
R.drawable.ic_settings_storage),
new SearchIndexableResource(9, R.xml.power_usage_summary,
PowerUsageSummary.class.getName(),
R.drawable.ic_settings_battery),
new SearchIndexableResource(10, R.xml.user_settings,
UserSettings.class.getName(),
R.drawable.ic_settings_multiuser),
new SearchIndexableResource(11, R.xml.location_settings,
LocationSettings.class.getName(),
R.drawable.ic_settings_location),
new SearchIndexableResource(12, R.xml.security_settings,
SecuritySettings.class.getName(),
R.drawable.ic_settings_security),
new SearchIndexableResource(13, R.xml.language_settings,
InputMethodAndLanguageSettings.class.getName(),
R.drawable.ic_settings_language),
new SearchIndexableResource(14, R.xml.privacy_settings,
PrivacySettings.class.getName(),
R.drawable.ic_settings_backup),
new SearchIndexableResource(15, R.xml.date_time_prefs,
DateTimeSettings.class.getName(),
R.drawable.ic_settings_date_time),
new SearchIndexableResource(16, R.xml.accessibility_settings,
AccessibilitySettings.class.getName(),
R.drawable.ic_settings_accessibility),
new SearchIndexableResource(17, R.xml.print_settings,
PrintSettingsFragment.class.getName(),
com.android.internal.R.drawable.ic_print),
new SearchIndexableResource(18, R.xml.development_prefs,
DevelopmentSettings.class.getName(),
R.drawable.ic_settings_development),
new SearchIndexableResource(19, R.xml.device_info_settings,
DeviceInfoSettings.class.getName(),
R.drawable.ic_settings_about),
};
@Override @Override
public boolean onCreate() { public boolean onCreate() {
return true; return true;
@@ -121,13 +58,13 @@ public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
@Override @Override
public Cursor queryXmlResources(String[] projection) { public Cursor queryXmlResources(String[] projection) {
MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS); MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
final int count = INDEXABLE_REFS.length; Collection<SearchIndexableResource> values = SearchIndexableResources.values();
for (int n = 0; n < count; n++) { for (SearchIndexableResource val : values) {
Object[] ref = new Object[7]; Object[] ref = new Object[7];
ref[0] = INDEXABLE_REFS[n].rank; ref[0] = val.rank;
ref[1] = INDEXABLE_REFS[n].xmlResId; ref[1] = val.xmlResId;
ref[2] = INDEXABLE_REFS[n].className; ref[2] = val.className;
ref[3] = INDEXABLE_REFS[n].iconResId; ref[3] = val.iconResId;
ref[4] = null; // intent action ref[4] = null; // intent action
ref[5] = null; // intent target package ref[5] = null; // intent target package
ref[6] = null; // intent target class ref[6] = null; // intent target class

View File

@@ -1168,12 +1168,13 @@ public class WifiSettings extends RestrictedSettingsFragment
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new SearchIndexProvider() { new SearchIndexProvider() {
@Override @Override
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context) { public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
return null; return null;
} }
@Override @Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context) { public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>(); final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
final Resources res = context.getResources(); final Resources res = context.getResources();