diff options
Diffstat (limited to 'src/com/android/settings/search/Index.java')
-rw-r--r-- | src/com/android/settings/search/Index.java | 681 |
1 files changed, 433 insertions, 248 deletions
diff --git a/src/com/android/settings/search/Index.java b/src/com/android/settings/search/Index.java index 987539b..6f91981 100644 --- a/src/com/android/settings/search/Index.java +++ b/src/com/android/settings/search/Index.java @@ -20,6 +20,8 @@ import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.TypedArray; @@ -44,11 +46,37 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_RANK; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY; + +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS; + import static com.android.settings.search.IndexDatabaseHelper.Tables; import static com.android.settings.search.IndexDatabaseHelper.IndexColumns; @@ -57,20 +85,22 @@ public class Index { private static final String LOG_TAG = "Index"; // Those indices should match the indices of SELECT_COLUMNS ! + public static final int COLUMN_INDEX_RANK = 0; public static final int COLUMN_INDEX_TITLE = 1; public static final int COLUMN_INDEX_SUMMARY_ON = 2; public static final int COLUMN_INDEX_SUMMARY_OFF = 3; public static final int COLUMN_INDEX_ENTRIES = 4; - public static final int COLUMN_INDEX_SWITCH_ON = 5; - public static final int COLUMN_INDEX_SWITCH_OFF = 6; - public static final int COLUMN_INDEX_KEYWORDS = 7; - public static final int COLUMN_INDEX_CLASS_NAME = 8; - public static final int COLUMN_INDEX_SCREEN_TITLE = 9; - public static final int COLUMN_INDEX_ICON = 10; - public static final int COLUMN_INDEX_INTENT_ACTION = 11; - public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 12; - public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 13; - public static final int COLUMN_INDEX_ENABLED = 14; + public static final int COLUMN_INDEX_KEYWORDS = 5; + public static final int COLUMN_INDEX_CLASS_NAME = 6; + public static final int COLUMN_INDEX_SCREEN_TITLE = 7; + public static final int COLUMN_INDEX_ICON = 8; + public static final int COLUMN_INDEX_INTENT_ACTION = 9; + public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 10; + public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 11; + public static final int COLUMN_INDEX_ENABLED = 12; + public static final int COLUMN_INDEX_KEY = 13; + + public static final String ENTRIES_SEPARATOR = "|"; // If you change the order of columns here, you SHOULD change the COLUMN_INDEX_XXX values private static final String[] SELECT_COLUMNS = new String[] { @@ -79,15 +109,15 @@ public class Index { IndexColumns.DATA_SUMMARY_ON, // 2 IndexColumns.DATA_SUMMARY_OFF, // 3 IndexColumns.DATA_ENTRIES, // 4 - IndexColumns.DATA_SWITCH_ON, // 5 - IndexColumns.DATA_SWITCH_OFF, // 6 - IndexColumns.DATA_KEYWORDS, // 7 - IndexColumns.CLASS_NAME, // 8 - IndexColumns.SCREEN_TITLE, // 9 - IndexColumns.ICON, // 10 - IndexColumns.INTENT_ACTION, // 11 - IndexColumns.INTENT_TARGET_PACKAGE, // 12 - IndexColumns.INTENT_TARGET_CLASS // 13 + IndexColumns.DATA_KEYWORDS, // 5 + IndexColumns.CLASS_NAME, // 6 + IndexColumns.SCREEN_TITLE, // 7 + IndexColumns.ICON, // 8 + IndexColumns.INTENT_ACTION, // 9 + IndexColumns.INTENT_TARGET_PACKAGE, // 10 + IndexColumns.INTENT_TARGET_CLASS, // 11 + IndexColumns.ENABLED, // 12 + IndexColumns.DATA_KEY_REF // 13 }; private static final String[] MATCH_COLUMNS = { @@ -97,10 +127,6 @@ public class Index { IndexColumns.DATA_SUMMARY_ON_NORMALIZED, IndexColumns.DATA_SUMMARY_OFF, IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, - IndexColumns.DATA_SWITCH_ON, - IndexColumns.DATA_SWITCH_ON_NORMALIZED, - IndexColumns.DATA_SWITCH_OFF, - IndexColumns.DATA_SWITCH_OFF_NORMALIZED, IndexColumns.DATA_ENTRIES, IndexColumns.DATA_KEYWORDS }; @@ -115,7 +141,8 @@ public class Index { 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 static final String NODE_NAME_SWITCH_PREFERENCE = "SwitchPreference"; + + private static final List<String> EMPTY_LIST = Collections.<String>emptyList(); private static Index sInstance; private final AtomicBoolean mIsAvailable = new AtomicBoolean(false); @@ -127,17 +154,21 @@ public class Index { */ private class UpdateData { public List<SearchIndexableData> dataToUpdate; - public List<String> dataToDelete; + public List<SearchIndexableData> dataToDelete; + public Map<String, List<String>> nonIndexableKeys; + public boolean forceUpdate = false; public UpdateData() { dataToUpdate = new ArrayList<SearchIndexableData>(); - dataToDelete = new ArrayList<String>(); + dataToDelete = new ArrayList<SearchIndexableData>(); + nonIndexableKeys = new HashMap<String, List<String>>(); } public void clear() { dataToUpdate.clear(); dataToDelete.clear(); + nonIndexableKeys.clear(); forceUpdate = false; } } @@ -172,6 +203,94 @@ public class Index { return getReadableDatabase().rawQuery(sql, null); } + public boolean update() { + final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE); + List<ResolveInfo> list = + mContext.getPackageManager().queryIntentContentProviders(intent, 0); + + final int size = list.size(); + for (int n = 0; n < size; n++) { + final ResolveInfo info = list.get(n); + if (!isWellKnownProvider(info)) { + continue; + } + final String authority = info.providerInfo.authority; + final String packageName = info.providerInfo.packageName; + + addIndexablesFromRemoteProvider(packageName, authority); + addNonIndexablesKeysFromRemoteProvider(packageName, authority); + } + + return updateInternal(); + } + + private boolean addIndexablesFromRemoteProvider(String packageName, String authority) { + try { + final Context packageContext = mContext.createPackageContext(packageName, 0); + + final Uri uriForResources = buildUriForXmlResources(authority); + addIndexablesForXmlResourceUri(packageContext, packageName, uriForResources, + SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS); + + final Uri uriForRawData = buildUriForRawData(authority); + addIndexablesForRawDataUri(packageContext, packageName, uriForRawData, + SearchIndexablesContract.INDEXABLES_RAW_COLUMNS); + return true; + } catch (PackageManager.NameNotFoundException e) { + Log.w(LOG_TAG, "Could not create context for " + packageName + ": " + + Log.getStackTraceString(e)); + return false; + } + } + + private void addNonIndexablesKeysFromRemoteProvider(String packageName, + String authority) { + final List<String> keys = + getNonIndexablesKeysFromRemoteProvider(packageName, authority); + addNonIndexableKeys(packageName, keys); + } + + private List<String> getNonIndexablesKeysFromRemoteProvider(String packageName, + String authority) { + try { + final Context packageContext = mContext.createPackageContext(packageName, 0); + + final Uri uriForNonIndexableKeys = buildUriForNonIndexableKeys(authority); + return getNonIndexablesKeys(packageContext, uriForNonIndexableKeys, + SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS); + } catch (PackageManager.NameNotFoundException e) { + Log.w(LOG_TAG, "Could not create context for " + packageName + ": " + + Log.getStackTraceString(e)); + return EMPTY_LIST; + } + } + + private List<String> getNonIndexablesKeys(Context packageContext, Uri uri, + String[] projection) { + + final ContentResolver resolver = packageContext.getContentResolver(); + final Cursor cursor = resolver.query(uri, projection, null, null, null); + + if (cursor == null) { + Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString()); + return EMPTY_LIST; + } + + List<String> result = new ArrayList<String>(); + try { + final int count = cursor.getCount(); + if (count > 0) { + while (cursor.moveToNext()) { + final String key = cursor.getString(COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE); + result.add(key); + } + } + return result; + } finally { + cursor.close(); + } + } + public void addIndexableData(SearchIndexableData data) { synchronized (mDataToProcess) { mDataToProcess.dataToUpdate.add(data); @@ -187,29 +306,55 @@ public class Index { } } - public void deleteIndexableData(String[] array) { + public void deleteIndexableData(SearchIndexableData data) { synchronized (mDataToProcess) { - final int count = array.length; - for (int n = 0; n < count; n++) { - mDataToProcess.dataToDelete.add(array[n]); - } + mDataToProcess.dataToDelete.add(data); } } - public boolean update() { - final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE); - List<ResolveInfo> list = - mContext.getPackageManager().queryIntentContentProviders(intent, 0); + public void addNonIndexableKeys(String authority, List<String> keys) { + synchronized (mDataToProcess) { + mDataToProcess.nonIndexableKeys.put(authority, keys); + } + } - final int size = list.size(); - for (int n = 0; n < size; n++) { - final ResolveInfo info = list.get(n); - final String authority = info.providerInfo.authority; - final String packageName = info.providerInfo.packageName; - addIndexablesFromRemoteProvider(packageName, authority); + /** + * Only allow a "well known" SearchIndexablesProvider. The provider should: + * + * - have read/write {@link android.Manifest.permission#READ_SEARCH_INDEXABLES} + * - be from a privileged package + */ + private boolean isWellKnownProvider(ResolveInfo info) { + final String authority = info.providerInfo.authority; + final String packageName = info.providerInfo.applicationInfo.packageName; + + if (TextUtils.isEmpty(authority) || TextUtils.isEmpty(packageName)) { + return false; } - return updateInternal(); + final String readPermission = info.providerInfo.readPermission; + final String writePermission = info.providerInfo.writePermission; + + if (TextUtils.isEmpty(readPermission) || TextUtils.isEmpty(writePermission)) { + return false; + } + + if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(readPermission) || + !android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(writePermission)) { + return false; + } + + return isPrivilegedPackage(packageName); + } + + private boolean isPrivilegedPackage(String packageName) { + final PackageManager pm = mContext.getPackageManager(); + try { + PackageInfo packInfo = pm.getPackageInfo(packageName, 0); + return ((packInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0); + } catch (PackageManager.NameNotFoundException e) { + return false; + } } public boolean updateFromRemoteProvider(String packageName, String authority) { @@ -219,7 +364,17 @@ public class Index { return updateInternal(); } - public boolean updateFromClassNameResource(String className, boolean includeInSearchResults) { + /** + * Update the Index for a specific class name resources + * + * @param className the class name (typically a fragment name). + * @param rebuild true means that you want to delete the data from the Index first. + * @param includeInSearchResults true means that you want the bit "enabled" set so that the + * data will be seen included into the search results + * @return true of the Index update has been successful. + */ + public boolean updateFromClassNameResource(String className, boolean rebuild, + boolean includeInSearchResults) { if (className == null) { throw new IllegalArgumentException("class name cannot be null!"); } @@ -229,6 +384,9 @@ public class Index { return false; } res.enabled = includeInSearchResults; + if (rebuild) { + deleteIndexableData(res); + } addIndexableData(res); mDataToProcess.forceUpdate = true; boolean result = updateInternal(); @@ -236,8 +394,9 @@ public class Index { return result; } - private boolean updateFromSearchIndexableData(SearchIndexableData data) { + public boolean updateFromSearchIndexableData(SearchIndexableData data) { addIndexableData(data); + mDataToProcess.forceUpdate = true; return updateInternal(); } @@ -249,26 +408,6 @@ public class Index { return IndexDatabaseHelper.getInstance(mContext).getWritableDatabase(); } - private boolean addIndexablesFromRemoteProvider(String packageName, String authority) { - final Context packageContext; - try { - packageContext = mContext.createPackageContext(packageName, 0); - - final Uri uriForResources = buildUriForXmlResources(authority); - addIndexablesForXmlResourceUri(packageContext, packageName, uriForResources, - SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS); - - final Uri uriForRawData = buildUriForRawData(authority); - addIndexablesForRawDataUri(packageContext, packageName, uriForRawData, - SearchIndexablesContract.INDEXABLES_RAW_COLUMNS); - return true; - } catch (PackageManager.NameNotFoundException e) { - Log.w(LOG_TAG, "Could not create context for " + packageName + ": " - + Log.getStackTraceString(e)); - return false; - } - } - private static Uri buildUriForXmlResources(String authority) { return Uri.parse("content://" + authority + "/" + SearchIndexablesContract.INDEXABLES_XML_RES_PATH); @@ -279,6 +418,11 @@ public class Index { SearchIndexablesContract.INDEXABLES_RAW_PATH); } + private static Uri buildUriForNonIndexableKeys(String authority) { + return Uri.parse("content://" + authority + "/" + + SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH); + } + private boolean updateInternal() { synchronized (mDataToProcess) { final UpdateIndexTask task = new UpdateIndexTask(); @@ -301,8 +445,7 @@ public class Index { Uri uri, String[] projection) { final ContentResolver resolver = packageContext.getContentResolver(); - final Cursor cursor = resolver.query(uri, projection, - null, null, null); + final Cursor cursor = resolver.query(uri, projection, null, null, null); if (cursor == null) { Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString()); @@ -313,15 +456,17 @@ public class Index { final int count = cursor.getCount(); if (count > 0) { while (cursor.moveToNext()) { - final int rank = cursor.getInt(0); - final int xmlResId = cursor.getInt(1); + final int rank = cursor.getInt(COLUMN_INDEX_XML_RES_RANK); + final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID); - final String className = cursor.getString(2); - final int iconResId = cursor.getInt(3); + final String className = cursor.getString(COLUMN_INDEX_XML_RES_CLASS_NAME); + final int iconResId = cursor.getInt(COLUMN_INDEX_XML_RES_ICON_RESID); - final String action = cursor.getString(4); - final String targetPackage = cursor.getString(5); - final String targetClass = cursor.getString(6); + final String action = cursor.getString(COLUMN_INDEX_XML_RES_INTENT_ACTION); + final String targetPackage = cursor.getString( + COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE); + final String targetClass = cursor.getString( + COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS); SearchIndexableResource sir = new SearchIndexableResource(packageContext); sir.rank = rank; @@ -345,8 +490,7 @@ public class Index { Uri uri, String[] projection) { final ContentResolver resolver = packageContext.getContentResolver(); - final Cursor cursor = resolver.query(uri, projection, - null, null, null); + final Cursor cursor = resolver.query(uri, projection, null, null, null); if (cursor == null) { Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString()); @@ -357,23 +501,25 @@ public class Index { final int count = cursor.getCount(); if (count > 0) { while (cursor.moveToNext()) { - final int rank = cursor.getInt(0); - final String title = cursor.getString(1); - final String summaryOn = cursor.getString(2); - final String summaryOff = cursor.getString(3); - final String entries = cursor.getString(4); - final String switchOn = cursor.getString(5); - final String switchOff = cursor.getString(6); - final String keywords = cursor.getString(7); + final int rank = cursor.getInt(COLUMN_INDEX_RAW_RANK); + final String title = cursor.getString(COLUMN_INDEX_RAW_TITLE); + final String summaryOn = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_ON); + final String summaryOff = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_OFF); + final String entries = cursor.getString(COLUMN_INDEX_RAW_ENTRIES); + final String keywords = cursor.getString(COLUMN_INDEX_RAW_KEYWORDS); - final String screenTitle = cursor.getString(8); + final String screenTitle = cursor.getString(COLUMN_INDEX_RAW_SCREEN_TITLE); - final String className = cursor.getString(9); - final int iconResId = cursor.getInt(10); + final String className = cursor.getString(COLUMN_INDEX_RAW_CLASS_NAME); + final int iconResId = cursor.getInt(COLUMN_INDEX_RAW_ICON_RESID); - final String action = cursor.getString(11); - final String targetPackage = cursor.getString(12); - final String targetClass = cursor.getString(13); + final String action = cursor.getString(COLUMN_INDEX_RAW_INTENT_ACTION); + final String targetPackage = cursor.getString( + COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE); + final String targetClass = cursor.getString( + COLUMN_INDEX_RAW_INTENT_TARGET_CLASS); + + final String key = cursor.getString(COLUMN_INDEX_RAW_KEY); SearchIndexableRaw data = new SearchIndexableRaw(packageContext); data.rank = rank; @@ -381,8 +527,6 @@ public class Index { data.summaryOn = summaryOn; data.summaryOff = summaryOff; data.entries = entries; - data.switchOn = switchOn; - data.switchOff = switchOff; data.keywords = keywords; data.screenTitle = screenTitle; data.className = className; @@ -391,6 +535,7 @@ public class Index { data.intentAction = action; data.intentTargetPackage = targetPackage; data.intentTargetClass = targetClass; + data.key = key; addIndexableData(data); } @@ -455,29 +600,95 @@ public class Index { } private void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr, - SearchIndexableData data) { + SearchIndexableData data, Map<String, List<String>> nonIndexableKeys) { if (data instanceof SearchIndexableResource) { - indexOneResource(database, localeStr, (SearchIndexableResource) data); + indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys); } else if (data instanceof SearchIndexableRaw) { indexOneRaw(database, localeStr, (SearchIndexableRaw) data); } } + 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.summaryOn, + raw.summaryOff, + raw.entries, + raw.className, + raw.screenTitle, + raw.iconResId, + raw.rank, + raw.keywords, + raw.intentAction, + raw.intentTargetPackage, + raw.intentTargetClass, + raw.enabled, + raw.key); + } + private void indexOneResource(SQLiteDatabase database, String localeStr, - SearchIndexableResource sir) { - if (sir.xmlResId > 0) { + SearchIndexableResource sir, Map<String, List<String>> nonIndexableKeysFromResource) { + + if (sir == null) { + Log.e(LOG_TAG, "Cannot index a null resource!"); + return; + } + + // Will be non null only for a Local provider + final Indexable.SearchIndexProvider provider = + TextUtils.isEmpty(sir.className) ? null : getSearchIndexProvider(sir.className); + + List<String> nonIndexableKeys = new ArrayList<String>(); + + if (sir.xmlResId > SearchIndexableResources.NO_DATA_RES_ID) { + List<String> resNonIndxableKeys = nonIndexableKeysFromResource.get(sir.packageName); + if (resNonIndxableKeys != null && resNonIndxableKeys.size() > 0) { + nonIndexableKeys.addAll(resNonIndxableKeys); + } indexFromResource(sir.context, database, localeStr, sir.xmlResId, sir.className, sir.iconResId, sir.rank, - sir.intentAction, sir.intentTargetPackage, sir.intentTargetClass); + sir.intentAction, sir.intentTargetPackage, sir.intentTargetClass, + nonIndexableKeys); } else if (!TextUtils.isEmpty(sir.className)) { - sir.context = mContext; - indexFromLocalProvider(database, localeStr, sir); + if (provider != null) { + List<String> providerNonIndexableKeys = provider.getNonIndexableKeys(sir.context); + if (providerNonIndexableKeys != null && providerNonIndexableKeys.size() > 0) { + nonIndexableKeys.addAll(providerNonIndexableKeys); + } + } + indexFromLocalProvider(mContext, database, localeStr, provider, sir.className, + sir.iconResId, sir.rank, sir.enabled, nonIndexableKeys); + } + } + + private Indexable.SearchIndexProvider getSearchIndexProvider(String className) { + try { + final Class<?> clazz = Class.forName(className); + if (Indexable.class.isAssignableFrom(clazz)) { + final Field f = clazz.getField(FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER); + return (Indexable.SearchIndexProvider) f.get(null); + } + } catch (ClassNotFoundException e) { + Log.e(LOG_TAG, "Cannot find class: " + className, e); + } catch (NoSuchFieldException e) { + Log.e(LOG_TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'", e); + } catch (IllegalAccessException e) { + Log.e(LOG_TAG, + "Illegal access to field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'", e); } + return null; } private void indexFromResource(Context context, SQLiteDatabase database, String localeStr, - int xmlResId, String fragmentName, int iconResId, int rank, - String intentAction, String intentTargetPackage, String intentTargetClass) { + int xmlResId, String fragmentName, int iconResId, int rank, + String intentAction, String intentTargetPackage, String intentTargetClass, + List<String> nonIndexableKeys) { XmlResourceParser parser = null; try { @@ -498,17 +709,26 @@ public class Index { final int outerDepth = parser.getDepth(); final AttributeSet attrs = Xml.asAttributeSet(parser); + final String screenTitle = getDataTitle(context, attrs); - String title = getDataTitle(context, attrs); - String summary = getDataSummary(context, attrs); - String keywords = getDataKeywords(context, attrs); + String key = getDataKey(context, attrs); + + String title; + String summary; + String keywords; // Insert rows for the main PreferenceScreen node. Rewrite the data for removing // hyphens. - updateOneRowWithFilteredData(database, localeStr, title, summary, null, null, - null, null, fragmentName, screenTitle, iconResId, rank, keywords, - intentAction, intentTargetPackage, intentTargetClass, true); + if (!nonIndexableKeys.contains(key)) { + title = getDataTitle(context, attrs); + summary = getDataSummary(context, attrs); + keywords = getDataKeywords(context, attrs); + + updateOneRowWithFilteredData(database, localeStr, title, summary, null, null, + fragmentName, screenTitle, iconResId, rank, + keywords, intentAction, intentTargetPackage, intentTargetClass, true, key); + } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { @@ -518,6 +738,11 @@ public class Index { nodeName = parser.getName(); + key = getDataKey(context, attrs); + if (nonIndexableKeys.contains(key)) { + continue; + } + title = getDataTitle(context, attrs); keywords = getDataKeywords(context, attrs); @@ -525,27 +750,24 @@ public class Index { summary = getDataSummary(context, attrs); String entries = null; - String switchOn = null; - String switchOff = null; if (nodeName.endsWith(NODE_NAME_LIST_PREFERENCE)) { entries = getDataEntries(context, attrs); - } else if (nodeName.endsWith(NODE_NAME_SWITCH_PREFERENCE)) { - switchOn = getDataSwitchOn(context, attrs); - switchOff = getDataSwitchOff(context, attrs); } // Insert rows for the child nodes of PreferenceScreen updateOneRowWithFilteredData(database, localeStr, title, summary, null, entries, - switchOn, switchOff, fragmentName, screenTitle, iconResId, rank, keywords, - intentAction, intentTargetPackage, intentTargetClass, true); - } else if (nodeName.equals(NODE_NAME_CHECK_BOX_PREFERENCE)) { + fragmentName, screenTitle, iconResId, rank, + keywords, intentAction, intentTargetPackage, intentTargetClass, + true, key); + } else { final String summaryOn = getDataSummaryOn(context, attrs); final String summaryOff = getDataSummaryOff(context, attrs); updateOneRowWithFilteredData(database, localeStr, title, summaryOn, summaryOff, - null, null, null, fragmentName, screenTitle, iconResId, rank, keywords, - intentAction, intentTargetPackage, intentTargetClass, true); + null, fragmentName, screenTitle, iconResId, rank, + keywords, intentAction, intentTargetPackage, intentTargetClass, + true, key); } } @@ -558,106 +780,80 @@ public class Index { } } - 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)) { + private void indexFromLocalProvider(Context context, SQLiteDatabase database, String localeStr, + Indexable.SearchIndexProvider provider, String className, int iconResId, int rank, + boolean enabled, List<String> nonIndexableKeys) { + + if (provider == null) { + Log.w(LOG_TAG, "Cannot find provider: " + className); return; } - updateOneRowWithFilteredData(database, localeStr, - raw.title, - raw.summaryOn, - raw.summaryOff, - raw.entries, - raw.switchOn, - raw.switchOff, - raw.className, - raw.screenTitle, - raw.iconResId, - raw.rank, - raw.keywords, - raw.intentAction, - raw.intentTargetPackage, - raw.intentTargetClass, - raw.enabled); - } + final List<SearchIndexableRaw> rawList = provider.getRawDataToIndex(context, 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(FIELD_NAME_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; - } + if (rawList != null) { + final int rawSize = rawList.size(); + for (int i = 0; i < rawSize; i++) { + SearchIndexableRaw raw = rawList.get(i); - updateOneRowWithFilteredData(database, localeStr, - raw.title, - raw.summaryOn, - raw.summaryOff, - raw.entries, - raw.switchOn, - raw.switchOff, - sir.className, - raw.screenTitle, - sir.iconResId, - sir.rank, - raw.keywords, - raw.intentAction, - raw.intentTargetPackage, - raw.intentTargetClass, - raw.enabled); - } + // Should be the same locale as the one we are processing + if (!raw.locale.toString().equalsIgnoreCase(localeStr)) { + continue; } - 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); + if (nonIndexableKeys.contains(raw.key)) { + continue; + } - // Should be the same locale as the one we are processing - if (!item.locale.toString().equalsIgnoreCase(localeStr)) { - continue; - } + updateOneRowWithFilteredData(database, localeStr, + raw.title, + raw.summaryOn, + raw.summaryOff, + raw.entries, + className, + raw.screenTitle, + iconResId, + rank, + raw.keywords, + raw.intentAction, + raw.intentTargetPackage, + raw.intentTargetClass, + raw.enabled, + raw.key); + } + } - indexFromResource(sir.context, database, localeStr, - item.xmlResId, item.className, item.iconResId, item.rank, - item.intentAction, item.intentTargetPackage, - item.intentTargetClass); - } + final List<SearchIndexableResource> resList = + provider.getXmlResourcesToIndex(context, 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; } + + final int itemIconResId = (item.iconResId == 0) ? iconResId : item.iconResId; + final int itemRank = (item.rank == 0) ? rank : item.rank; + String itemClassName = (TextUtils.isEmpty(item.className)) + ? className : item.className; + + indexFromResource(context, database, localeStr, + item.xmlResId, itemClassName, itemIconResId, itemRank, + item.intentAction, item.intentTargetPackage, + item.intentTargetClass, nonIndexableKeys); } - } catch (ClassNotFoundException e) { - Log.e(LOG_TAG, "Cannot find class: " + sir.className, e); - } catch (NoSuchFieldException e) { - Log.e(LOG_TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'", e); - } catch (IllegalAccessException e) { - Log.e(LOG_TAG, - "Illegal access to field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'", e); } } private void updateOneRowWithFilteredData(SQLiteDatabase database, String locale, String title, String summaryOn, String summaryOff, String entries, - String switchOn, String switchOff, String className, + String className, String screenTitle, int iconResId, int rank, String keywords, String intentAction, String intentTargetPackage, String intentTargetClass, - boolean enabled) { + boolean enabled, String key) { String updatedTitle; if (title != null) { @@ -681,43 +877,24 @@ public class Index { updatedSummaryOff = EMPTY; } - String updatedSwitchOn; - if (switchOn != null) { - updatedSwitchOn = switchOn.replaceAll(NON_BREAKING_HYPHEN, HYPHEN); - } else { - updatedSwitchOn = EMPTY; - } - - String updatedSwitchOff; - if (switchOff != null) { - updatedSwitchOff = switchOff.replaceAll(NON_BREAKING_HYPHEN, HYPHEN); - } else { - updatedSwitchOff = EMPTY; - } - String normalizedTitle = updatedTitle.replaceAll(HYPHEN, EMPTY); String normalizedSummaryOn = updatedSummaryOn.replaceAll(HYPHEN, EMPTY); String normalizedSummaryOff = updatedSummaryOff.replaceAll(HYPHEN, EMPTY); - String normalizedSwitchOn = updatedSwitchOn.replaceAll(HYPHEN, EMPTY); - String normalizedSwitchOff = updatedSwitchOff.replaceAll(HYPHEN, EMPTY); updateOneRow(database, locale, updatedTitle, normalizedTitle, updatedSummaryOn, normalizedSummaryOn, updatedSummaryOff, normalizedSummaryOff, entries, - updatedSwitchOn, normalizedSwitchOn, updatedSwitchOff, normalizedSwitchOff, className, screenTitle, iconResId, - rank, keywords, intentAction, intentTargetPackage, intentTargetClass, enabled); + rank, keywords, intentAction, intentTargetPackage, intentTargetClass, enabled, key); } private void updateOneRow(SQLiteDatabase database, String locale, String updatedTitle, String normalizedTitle, String updatedSummaryOn, String normalizedSummaryOn, String updatedSummaryOff, String normalizedSummaryOff, String entries, - String updatedSwitchOn, String normalizedSwitchOn, - String updatedSwitchOff, String normalizedSwitchOff, String className, String screenTitle, int iconResId, int rank, String keywords, String intentAction, String intentTargetPackage, String intentTargetClass, - boolean enabled) { + boolean enabled, String key) { if (TextUtils.isEmpty(updatedTitle)) { return; @@ -734,10 +911,6 @@ public class Index { values.put(IndexColumns.DATA_SUMMARY_OFF, updatedSummaryOff); values.put(IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, normalizedSummaryOff); values.put(IndexColumns.DATA_ENTRIES, entries); - values.put(IndexColumns.DATA_SWITCH_ON, updatedSwitchOn); - values.put(IndexColumns.DATA_SWITCH_ON_NORMALIZED, normalizedSwitchOn); - values.put(IndexColumns.DATA_SWITCH_OFF, updatedSwitchOff); - values.put(IndexColumns.DATA_SWITCH_OFF_NORMALIZED, normalizedSwitchOff); values.put(IndexColumns.DATA_KEYWORDS, keywords); values.put(IndexColumns.CLASS_NAME, className); values.put(IndexColumns.SCREEN_TITLE, screenTitle); @@ -746,10 +919,17 @@ public class Index { values.put(IndexColumns.INTENT_TARGET_CLASS, intentTargetClass); values.put(IndexColumns.ICON, iconResId); values.put(IndexColumns.ENABLED, enabled); + values.put(IndexColumns.DATA_KEY_REF, key); database.replaceOrThrow(Tables.TABLE_PREFS_INDEX, null, values); } + private String getDataKey(Context context, AttributeSet attrs) { + return getData(context, attrs, + com.android.internal.R.styleable.Preference, + com.android.internal.R.styleable.Preference_key); + } + private String getDataTitle(Context context, AttributeSet attrs) { return getData(context, attrs, com.android.internal.R.styleable.Preference, @@ -780,18 +960,6 @@ public class Index { com.android.internal.R.styleable.ListPreference_entries); } - private String getDataSwitchOn(Context context, AttributeSet attrs) { - return getData(context, attrs, - com.android.internal.R.styleable.SwitchPreference, - com.android.internal.R.styleable.SwitchPreference_switchTextOn); - } - - private String getDataSwitchOff(Context context, AttributeSet attrs) { - return getData(context, attrs, - com.android.internal.R.styleable.SwitchPreference, - com.android.internal.R.styleable.SwitchPreference_switchTextOff); - } - private String getDataKeywords(Context context, AttributeSet attrs) { return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_keywords); } @@ -828,7 +996,7 @@ public class Index { final StringBuilder result = new StringBuilder(); for (int n = 0; n < count; n++) { result.append(data[n]); - result.append(" "); + result.append(ENTRIES_SEPARATOR); } return result.toString(); } @@ -866,19 +1034,23 @@ public class Index { boolean result = false; final List<SearchIndexableData> dataToUpdate = params[0].dataToUpdate; - final List<String> dataToDelete = params[0].dataToDelete; + final List<SearchIndexableData> dataToDelete = params[0].dataToDelete; + final Map<String, List<String>> nonIndexableKeys = params[0].nonIndexableKeys; + 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); } + if (dataToUpdate.size() > 0) { + processDataToUpdate(database, localeStr, dataToUpdate, nonIndexableKeys, + forceUpdate); + } database.setTransactionSuccessful(); result = true; } finally { @@ -888,7 +1060,8 @@ public class Index { } private boolean processDataToUpdate(SQLiteDatabase database, String localeStr, - List<SearchIndexableData> dataToUpdate, boolean forceUpdate) { + List<SearchIndexableData> dataToUpdate, Map<String, List<String>> nonIndexableKeys, + boolean forceUpdate) { if (!forceUpdate && isLocaleAlreadyIndexed(database, localeStr)) { Log.d(LOG_TAG, "Locale '" + localeStr + "' is already indexed"); @@ -901,7 +1074,7 @@ public class Index { final int count = dataToUpdate.size(); for (int n = 0; n < count; n++) { final SearchIndexableData data = dataToUpdate.get(n); - indexOneSearchIndexableData(database, localeStr, data); + indexOneSearchIndexableData(database, localeStr, data, nonIndexableKeys); } final long now = System.currentTimeMillis(); @@ -911,15 +1084,27 @@ public class Index { } private boolean processDataToDelete(SQLiteDatabase database, String localeStr, - List<String> dataToDelete) { + List<SearchIndexableData> 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 SearchIndexableData data = dataToDelete.get(n); + if (data == null) { + continue; + } + if (!TextUtils.isEmpty(data.className)) { + delete(database, IndexColumns.CLASS_NAME, data.className); + } else { + if (data instanceof SearchIndexableRaw) { + final SearchIndexableRaw raw = (SearchIndexableRaw) data; + if (!TextUtils.isEmpty(raw.title)) { + delete(database, IndexColumns.DATA_TITLE, raw.title); + } + } + } } final long now = System.currentTimeMillis(); @@ -928,9 +1113,9 @@ public class Index { return result; } - private int delete(SQLiteDatabase database, String title) { - final String whereClause = IndexColumns.DATA_TITLE + "=?"; - final String[] whereArgs = new String[] { title }; + private int delete(SQLiteDatabase database, String columName, String value) { + final String whereClause = columName + "=?"; + final String[] whereArgs = new String[] { value }; return database.delete(Tables.TABLE_PREFS_INDEX, whereClause, whereArgs); } |