diff --git a/src/com/android/settings/core/PreferenceController.java b/src/com/android/settings/core/PreferenceController.java index 8f551dec98b..01c998aaa60 100644 --- a/src/com/android/settings/core/PreferenceController.java +++ b/src/com/android/settings/core/PreferenceController.java @@ -20,6 +20,7 @@ import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; import com.android.settings.search.SearchIndexableRaw; +import com.android.settings.search2.ResultPayload; import java.util.List; @@ -104,4 +105,11 @@ public abstract class PreferenceController { screen.removePreference(pref); } } + + /** + * @return the {@link ResultPayload} corresponding to the search result type for the preference. + */ + public ResultPayload getResultPayload() { + return null; + } } diff --git a/src/com/android/settings/search/BaseSearchIndexProvider.java b/src/com/android/settings/search/BaseSearchIndexProvider.java index 0fe1944e984..bb518ba6625 100644 --- a/src/com/android/settings/search/BaseSearchIndexProvider.java +++ b/src/com/android/settings/search/BaseSearchIndexProvider.java @@ -18,6 +18,7 @@ package com.android.settings.search; import android.content.Context; import android.provider.SearchIndexableResource; +import com.android.settings.core.PreferenceController; import java.util.Collections; import java.util.List; @@ -46,4 +47,9 @@ public class BaseSearchIndexProvider implements Indexable.SearchIndexProvider { public List getNonIndexableKeys(Context context) { return EMPTY_LIST; } + + @Override + public List getPreferenceControllers(Context context) { + return null; + } } diff --git a/src/com/android/settings/search/IndexDatabaseHelper.java b/src/com/android/settings/search/IndexDatabaseHelper.java index cddee683859..fcf5e7cf63e 100644 --- a/src/com/android/settings/search/IndexDatabaseHelper.java +++ b/src/com/android/settings/search/IndexDatabaseHelper.java @@ -28,46 +28,48 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper { private static final String TAG = "IndexDatabaseHelper"; private static final String DATABASE_NAME = "search_index.db"; - private static final int DATABASE_VERSION = 115; + private static final int DATABASE_VERSION = 116; private static final String INDEX = "index"; public interface Tables { - public static final String TABLE_PREFS_INDEX = "prefs_index"; - public static final String TABLE_META_INDEX = "meta_index"; - public static final String TABLE_SAVED_QUERIES = "saved_queries"; + String TABLE_PREFS_INDEX = "prefs_index"; + String TABLE_META_INDEX = "meta_index"; + String TABLE_SAVED_QUERIES = "saved_queries"; } public interface IndexColumns { - public static final String DOCID = "docid"; - public static final String LOCALE = "locale"; - public static final String DATA_RANK = "data_rank"; - public static final String DATA_TITLE = "data_title"; - public static final String DATA_TITLE_NORMALIZED = "data_title_normalized"; - public static final String DATA_SUMMARY_ON = "data_summary_on"; - public static final String DATA_SUMMARY_ON_NORMALIZED = "data_summary_on_normalized"; - public static final String DATA_SUMMARY_OFF = "data_summary_off"; - public static final String DATA_SUMMARY_OFF_NORMALIZED = "data_summary_off_normalized"; - public static final String DATA_ENTRIES = "data_entries"; - public static final String DATA_KEYWORDS = "data_keywords"; - public static final String CLASS_NAME = "class_name"; - public static final String SCREEN_TITLE = "screen_title"; - public static final String INTENT_ACTION = "intent_action"; - public static final String INTENT_TARGET_PACKAGE = "intent_target_package"; - public static final String INTENT_TARGET_CLASS = "intent_target_class"; - public static final String ICON = "icon"; - public static final String ENABLED = "enabled"; - public static final String DATA_KEY_REF = "data_key_reference"; - public static final String USER_ID = "user_id"; + String DOCID = "docid"; + String LOCALE = "locale"; + String DATA_RANK = "data_rank"; + String DATA_TITLE = "data_title"; + String DATA_TITLE_NORMALIZED = "data_title_normalized"; + String DATA_SUMMARY_ON = "data_summary_on"; + String DATA_SUMMARY_ON_NORMALIZED = "data_summary_on_normalized"; + String DATA_SUMMARY_OFF = "data_summary_off"; + String DATA_SUMMARY_OFF_NORMALIZED = "data_summary_off_normalized"; + String DATA_ENTRIES = "data_entries"; + String DATA_KEYWORDS = "data_keywords"; + String CLASS_NAME = "class_name"; + String SCREEN_TITLE = "screen_title"; + String INTENT_ACTION = "intent_action"; + String INTENT_TARGET_PACKAGE = "intent_target_package"; + String INTENT_TARGET_CLASS = "intent_target_class"; + String ICON = "icon"; + String ENABLED = "enabled"; + String DATA_KEY_REF = "data_key_reference"; + String USER_ID = "user_id"; + String PAYLOAD_TYPE = "payload_type"; + String PAYLOAD = "payload"; } public interface MetaColumns { - public static final String BUILD = "build"; + String BUILD = "build"; } public interface SavedQueriesColums { - public static final String QUERY = "query"; - public static final String TIME_STAMP = "timestamp"; + String QUERY = "query"; + String TIME_STAMP = "timestamp"; } private static final String CREATE_INDEX_TABLE = @@ -110,6 +112,10 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper { IndexColumns.DATA_KEY_REF + ", " + IndexColumns.USER_ID + + ", " + + IndexColumns.PAYLOAD_TYPE + + ", " + + IndexColumns.PAYLOAD + ");"; private static final String CREATE_META_TABLE = diff --git a/src/com/android/settings/search/Indexable.java b/src/com/android/settings/search/Indexable.java index e87d9dca9dd..2ec4ac055d0 100644 --- a/src/com/android/settings/search/Indexable.java +++ b/src/com/android/settings/search/Indexable.java @@ -18,6 +18,7 @@ package com.android.settings.search; import android.content.Context; import android.provider.SearchIndexableResource; +import com.android.settings.core.PreferenceController; import java.util.List; @@ -65,5 +66,11 @@ public interface Indexable { * @return a list of {@link SearchIndexableRaw} references. Can be null. */ List getNonIndexableKeys(Context context); + + /** + * @param context + * @return a list of {@link PreferenceController} for ResultPayload data during Indexing. + */ + List getPreferenceControllers(Context context); } } diff --git a/src/com/android/settings/search2/DatabaseIndexingManager.java b/src/com/android/settings/search2/DatabaseIndexingManager.java index 9bb4538aa2c..fa482efab8b 100644 --- a/src/com/android/settings/search2/DatabaseIndexingManager.java +++ b/src/com/android/settings/search2/DatabaseIndexingManager.java @@ -34,10 +34,12 @@ import android.provider.SearchIndexableData; import android.provider.SearchIndexableResource; import android.provider.SearchIndexablesContract; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; +import com.android.settings.core.PreferenceController; import com.android.settings.search.IndexDatabaseHelper; import com.android.settings.search.Indexable; import com.android.settings.search.Ranking; @@ -86,56 +88,6 @@ import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RES public class DatabaseIndexingManager { private static final String LOG_TAG = "DatabaseIndexingManager"; - // 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_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 int COLUMN_INDEX_USER_ID = 14; - - // If you change the order of columns here, you SHOULD change the COLUMN_INDEX_XXX values - private static final String[] SELECT_COLUMNS = new String[] { - IndexDatabaseHelper.IndexColumns.DATA_RANK, // 0 - IndexDatabaseHelper.IndexColumns.DATA_TITLE, // 1 - IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, // 2 - IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, // 3 - IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, // 4 - IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, // 5 - IndexDatabaseHelper.IndexColumns.CLASS_NAME, // 6 - IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, // 7 - IndexDatabaseHelper.IndexColumns.ICON, // 8 - IndexDatabaseHelper.IndexColumns.INTENT_ACTION, // 9 - IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, // 10 - IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, // 11 - IndexDatabaseHelper.IndexColumns.ENABLED, // 12 - IndexDatabaseHelper.IndexColumns.DATA_KEY_REF // 13 - }; - - private static final String[] MATCH_COLUMNS_PRIMARY = { - IndexDatabaseHelper.IndexColumns.DATA_TITLE, - IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, - IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS - }; - - private static final String[] MATCH_COLUMNS_SECONDARY = { - IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, - IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, - IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, - IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, - IndexDatabaseHelper.IndexColumns.DATA_ENTRIES - }; - - 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"; @@ -514,7 +466,7 @@ public class DatabaseIndexingManager { } } - private void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr, + public void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr, SearchIndexableData data, Map> nonIndexableKeys) { if (data instanceof SearchIndexableResource) { indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys); @@ -530,22 +482,22 @@ public class DatabaseIndexingManager { 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, - raw.userId); + 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(raw.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, @@ -622,25 +574,45 @@ public class DatabaseIndexingManager { final int outerDepth = parser.getDepth(); final AttributeSet attrs = Xml.asAttributeSet(parser); - final String screenTitle = XMLParserUtil.getDataTitle(context, attrs); + final String screenTitle = XmlParserUtils.getDataTitle(context, attrs); - String key = XMLParserUtil.getDataKey(context, attrs); + String key = XmlParserUtils.getDataKey(context, attrs); String title; String summary; String keywords; + ResultPayload payload; + + ArrayMap controllerUriMap = null; + + if (fragmentName != null) { + controllerUriMap = (ArrayMap) DatabaseIndexingUtils + .getPreferenceControllerUriMap(fragmentName, context); + } // Insert rows for the main PreferenceScreen node. Rewrite the data for removing // hyphens. if (!nonIndexableKeys.contains(key)) { - title = XMLParserUtil.getDataTitle(context, attrs); - summary = XMLParserUtil.getDataSummary(context, attrs); - keywords = XMLParserUtil.getDataKeywords(context, attrs); + title = XmlParserUtils.getDataTitle(context, attrs); + summary = XmlParserUtils.getDataSummary(context, attrs); + keywords = XmlParserUtils.getDataKeywords(context, attrs); - updateOneRowWithFilteredData(database, localeStr, title, summary, null, null, - fragmentName, screenTitle, iconResId, rank, - keywords, intentAction, intentTargetPackage, intentTargetClass, true, - key, -1 /* default user id */); + DatabaseRow.Builder builder = new DatabaseRow.Builder(); + builder.setLocale(localeStr) + .setEntries(null) + .setClassName(fragmentName) + .setScreenTitle(screenTitle) + .setIconResId(iconResId) + .setRank(rank) + .setIntentAction(intentAction) + .setIntentTargetPackage(intentTargetPackage) + .setIntentTargetClass(intentTargetClass) + .setEnabled(true) + .setKey(key) + .setUserId(-1 /* default user id */); + + updateOneRowWithFilteredData(database, builder, title, summary, + null /* summary off */, keywords); } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -651,43 +623,56 @@ public class DatabaseIndexingManager { nodeName = parser.getName(); - key = XMLParserUtil.getDataKey(context, attrs); + key = XmlParserUtils.getDataKey(context, attrs); if (nonIndexableKeys.contains(key)) { continue; } - title = XMLParserUtil.getDataTitle(context, attrs); - keywords = XMLParserUtil.getDataKeywords(context, attrs); + title = XmlParserUtils.getDataTitle(context, attrs); + keywords = XmlParserUtils.getDataKeywords(context, attrs); + + DatabaseRow.Builder builder = new DatabaseRow.Builder(); + builder.setLocale(localeStr) + .setClassName(fragmentName) + .setScreenTitle(screenTitle) + .setIconResId(iconResId) + .setRank(rank) + .setIntentAction(intentAction) + .setIntentTargetPackage(intentTargetPackage) + .setIntentTargetClass(intentTargetClass) + .setEnabled(true) + .setKey(key) + .setUserId(-1 /* default user id */); if (!nodeName.equals(NODE_NAME_CHECK_BOX_PREFERENCE)) { - summary = XMLParserUtil.getDataSummary(context, attrs); + summary = XmlParserUtils.getDataSummary(context, attrs); String entries = null; if (nodeName.endsWith(NODE_NAME_LIST_PREFERENCE)) { - entries = XMLParserUtil.getDataEntries(context, attrs); + entries = XmlParserUtils.getDataEntries(context, attrs); } + payload = DatabaseIndexingUtils.getPayloadFromUriMap(controllerUriMap, key); + + builder.setEntries(entries) + .setPayload(payload); + // Insert rows for the child nodes of PreferenceScreen - updateOneRowWithFilteredData(database, localeStr, title, summary, null, entries, - fragmentName, screenTitle, iconResId, rank, - keywords, intentAction, intentTargetPackage, intentTargetClass, - true, key, -1 /* default user id */); + updateOneRowWithFilteredData(database, builder, title, summary, + null /* summary off */, keywords); } else { - String summaryOn = XMLParserUtil.getDataSummaryOn(context, attrs); - String summaryOff = XMLParserUtil.getDataSummaryOff(context, attrs); + String summaryOn = XmlParserUtils.getDataSummaryOn(context, attrs); + String summaryOff = XmlParserUtils.getDataSummaryOff(context, attrs); if (TextUtils.isEmpty(summaryOn) && TextUtils.isEmpty(summaryOff)) { - summaryOn = XMLParserUtil.getDataSummary(context, attrs); + summaryOn = XmlParserUtils.getDataSummary(context, attrs); } - updateOneRowWithFilteredData(database, localeStr, title, summaryOn, summaryOff, - null, fragmentName, screenTitle, iconResId, rank, - keywords, intentAction, intentTargetPackage, intentTargetClass, - true, key, -1 /* default user id */); + updateOneRowWithFilteredData(database, builder, title, summaryOn, summaryOff, + keywords); } } - } catch (XmlPullParserException e) { throw new RuntimeException("Error parsing PreferenceScreen", e); } catch (IOException e) { @@ -709,6 +694,7 @@ public class DatabaseIndexingManager { final List rawList = provider.getRawDataToIndex(context, enabled); if (rawList != null) { + final int rawSize = rawList.size(); for (int i = 0; i < rawSize; i++) { SearchIndexableRaw raw = rawList.get(i); @@ -722,22 +708,22 @@ public class DatabaseIndexingManager { 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, - raw.userId); + DatabaseRow.Builder builder = new DatabaseRow.Builder(); + builder.setLocale(localeStr) + .setEntries(raw.entries) + .setClassName(className) + .setScreenTitle(raw.screenTitle) + .setIconResId(iconResId) + .setRank(rank) + .setIntentAction(raw.intentAction) + .setIntentTargetPackage(raw.intentTargetPackage) + .setIntentTargetClass(raw.intentTargetClass) + .setEnabled(raw.enabled) + .setKey(raw.key) + .setUserId(raw.userId); + + updateOneRowWithFilteredData(database, builder, raw.title, raw.summaryOn, + raw.summaryOff, raw.keywords); } } @@ -766,70 +752,68 @@ public class DatabaseIndexingManager { } } - private void updateOneRowWithFilteredData(SQLiteDatabase database, String locale, - String title, String summaryOn, String summaryOff, String entries, - String className, - String screenTitle, int iconResId, int rank, String keywords, - String intentAction, String intentTargetPackage, String intentTargetClass, - boolean enabled, String key, int userId) { + private void updateOneRowWithFilteredData(SQLiteDatabase database, DatabaseRow.Builder builder, + String title, String summaryOn, String summaryOff,String keywords) { - final String updatedTitle = XMLParserUtil.normalizeHyphen(title); - final String updatedSummaryOn = XMLParserUtil.normalizeHyphen(summaryOn); - final String updatedSummaryOff = XMLParserUtil.normalizeHyphen(summaryOff); + final String updatedTitle = DatabaseIndexingUtils.normalizeHyphen(title); + final String updatedSummaryOn = DatabaseIndexingUtils.normalizeHyphen(summaryOn); + final String updatedSummaryOff = DatabaseIndexingUtils.normalizeHyphen(summaryOff); - final String normalizedTitle = XMLParserUtil.normalizeString(updatedTitle); - final String normalizedSummaryOn = XMLParserUtil.normalizeString(updatedSummaryOn); - final String normalizedSummaryOff = XMLParserUtil.normalizeString(updatedSummaryOff); + final String normalizedTitle = DatabaseIndexingUtils.normalizeString(updatedTitle); + final String normalizedSummaryOn = DatabaseIndexingUtils.normalizeString(updatedSummaryOn); + final String normalizedSummaryOff = DatabaseIndexingUtils + .normalizeString(updatedSummaryOff); - final String spaceDelimitedKeywords = XMLParserUtil.normalizeKeywords(keywords); + final String spaceDelimitedKeywords = DatabaseIndexingUtils.normalizeKeywords(keywords); - updateOneRow(database, locale, - updatedTitle, normalizedTitle, updatedSummaryOn, normalizedSummaryOn, - updatedSummaryOff, normalizedSummaryOff, entries, className, screenTitle, iconResId, - rank, spaceDelimitedKeywords, intentAction, intentTargetPackage, intentTargetClass, - enabled, key, userId); + builder.setUpdatedTitle(updatedTitle) + .setUpdatedSummaryOn(updatedSummaryOn) + .setUpdatedSummaryOff(updatedSummaryOff) + .setNormalizedTitle(normalizedTitle) + .setNormalizedSummaryOn(normalizedSummaryOn) + .setNormalizedSummaryOff(normalizedSummaryOff) + .setSpaceDelimitedKeywords(spaceDelimitedKeywords); + + updateOneRow(database, builder.build()); } - private void updateOneRow(SQLiteDatabase database, String locale, String updatedTitle, - String normalizedTitle, String updatedSummaryOn, String normalizedSummaryOn, - String updatedSummaryOff, String normalizedSummaryOff, String entries, String className, - String screenTitle, int iconResId, int rank, String spaceDelimitedKeywords, - String intentAction, String intentTargetPackage, String intentTargetClass, - boolean enabled, String key, int userId) { + private void updateOneRow(SQLiteDatabase database, DatabaseRow row) { - if (TextUtils.isEmpty(updatedTitle)) { + if (TextUtils.isEmpty(row.updatedTitle)) { return; } // The DocID should contains more than the title string itself (you may have two settings // with the same title). So we need to use a combination of the title and the screenTitle. - StringBuilder sb = new StringBuilder(updatedTitle); - sb.append(screenTitle); + StringBuilder sb = new StringBuilder(row.updatedTitle); + sb.append(row.screenTitle); int docId = sb.toString().hashCode(); ContentValues values = new ContentValues(); values.put(IndexDatabaseHelper.IndexColumns.DOCID, docId); - values.put(IndexDatabaseHelper.IndexColumns.LOCALE, locale); - values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, rank); - values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, updatedTitle); - values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, normalizedTitle); - values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, updatedSummaryOn); + values.put(IndexDatabaseHelper.IndexColumns.LOCALE, row.locale); + values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, row.rank); + values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, row.updatedTitle); + values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, row.normalizedTitle); + values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, row.updatedSummaryOn); values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, - normalizedSummaryOn); - values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, updatedSummaryOff); + row.normalizedSummaryOn); + values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, row.updatedSummaryOff); values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, - normalizedSummaryOff); - values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, entries); - values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, spaceDelimitedKeywords); - values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME, className); - values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, screenTitle); - values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, intentAction); - values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, intentTargetPackage); - values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, intentTargetClass); - values.put(IndexDatabaseHelper.IndexColumns.ICON, iconResId); - values.put(IndexDatabaseHelper.IndexColumns.ENABLED, enabled); - values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, key); - values.put(IndexDatabaseHelper.IndexColumns.USER_ID, userId); + row.normalizedSummaryOff); + values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, row.entries); + values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, row.spaceDelimitedKeywords); + values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME, row.className); + values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, row.screenTitle); + values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, row.intentAction); + values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, row.intentTargetPackage); + values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, row.intentTargetClass); + values.put(IndexDatabaseHelper.IndexColumns.ICON, row.iconResId); + values.put(IndexDatabaseHelper.IndexColumns.ENABLED, row.enabled); + values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, row.key); + values.put(IndexDatabaseHelper.IndexColumns.USER_ID, row.userId); + values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, row.payloadType); + values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, row.payload); database.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values); } @@ -960,4 +944,195 @@ public class DatabaseIndexingManager { whereArgs); } } + + 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 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; + 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; + } + + 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 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 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; + } + + public DatabaseRow build() { + return new DatabaseRow(this); + } + } + } } diff --git a/src/com/android/settings/search2/DatabaseIndexingUtils.java b/src/com/android/settings/search2/DatabaseIndexingUtils.java index a8f64dfeaa8..c7a7f921f55 100644 --- a/src/com/android/settings/search2/DatabaseIndexingUtils.java +++ b/src/com/android/settings/search2/DatabaseIndexingUtils.java @@ -23,12 +23,19 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.net.Uri; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.Log; +import com.android.settings.core.PreferenceController; import com.android.settings.search.Indexable; import java.lang.reflect.Field; +import java.text.Normalizer; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; /** * Utility class for {@like DatabaseIndexingManager} to handle the mapping between Payloads @@ -36,17 +43,82 @@ import java.lang.reflect.Field; */ public class DatabaseIndexingUtils { - private static final String LOG_TAG = "IndexingUtil"; + private static final String TAG = "IndexingUtil"; private static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER = "SEARCH_INDEX_DATA_PROVIDER"; + private static final String NON_BREAKING_HYPHEN = "\u2011"; + private static final String EMPTY = ""; + private static final String LIST_DELIMITERS = "[,]\\s*"; + private static final String HYPHEN = "-"; + private static final String SPACE = " "; + + private static final Pattern REMOVE_DIACRITICALS_PATTERN + = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); + + /** + * @param className which wil provide the map between from {@link Uri}s to + * {@link PreferenceController} + * @param context + * @return A map between {@link Uri}s and {@link PreferenceController}s to get the payload + * types for Settings. + */ + public static Map getPreferenceControllerUriMap( + String className, Context context) { + final Class clazz = getIndexableClass(className); + + if (clazz == null) { + Log.d(TAG, "SearchIndexableResource '" + className + + "' should implement the " + Indexable.class.getName() + " interface!"); + return null; + } + + // Will be non null only for a Local provider implementing a + // SEARCH_INDEX_DATA_PROVIDER field + final Indexable.SearchIndexProvider provider = getSearchIndexProvider(clazz); + List controllers = + provider.getPreferenceControllers(context); + + if (controllers == null ) { + return null; + } + + ArrayMap map = new ArrayMap<>(); + + for (PreferenceController controller : controllers) { + map.put(controller.getPreferenceKey(), controller); + } + + return map; + } + + /** + * @param uriMap Map between the {@link PreferenceController} keys and the controllers themselves. + * @param key The look up key + * @return The Payload from the {@link PreferenceController} specified by the key, if it exists. + * Otherwise null. + */ + public static ResultPayload getPayloadFromUriMap(ArrayMap uriMap, + String key) { + if (uriMap == null) { + return null; + } + + PreferenceController controller = uriMap.get(key); + if (controller == null) { + return null; + } + + return controller.getResultPayload(); + } + public static Class getIndexableClass(String className) { final Class clazz; try { clazz = Class.forName(className); } catch (ClassNotFoundException e) { - Log.d(LOG_TAG, "Cannot find class: " + className); + Log.d(TAG, "Cannot find class: " + className); return null; } return isIndexableClass(clazz) ? clazz : null; @@ -61,15 +133,15 @@ public class DatabaseIndexingUtils { final Field f = clazz.getField(FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER); return (Indexable.SearchIndexProvider) f.get(null); } catch (NoSuchFieldException e) { - Log.d(LOG_TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'"); + Log.d(TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'"); } catch (SecurityException se) { - Log.d(LOG_TAG, + Log.d(TAG, "Security exception for field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'"); } catch (IllegalAccessException e) { - Log.d(LOG_TAG, + Log.d(TAG, "Illegal access to field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'"); } catch (IllegalArgumentException e) { - Log.d(LOG_TAG, + Log.d(TAG, "Illegal argument when accessing field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'"); } @@ -115,4 +187,19 @@ public class DatabaseIndexingUtils { return false; } } + + public static String normalizeHyphen(String input) { + return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY; + } + + 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(); + } + + public static String normalizeKeywords(String input) { + return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY; + } } diff --git a/src/com/android/settings/search2/IntentPayload.java b/src/com/android/settings/search2/IntentPayload.java index 1ef37973987..3e1049ef907 100644 --- a/src/com/android/settings/search2/IntentPayload.java +++ b/src/com/android/settings/search2/IntentPayload.java @@ -18,7 +18,6 @@ package com.android.settings.search2; import android.content.Intent; import android.os.Parcel; -import com.android.internal.annotations.VisibleForTesting; /** * Encapsulates the standard intent based results as seen in first party apps and Settings results. diff --git a/src/com/android/settings/search2/ResultPayloadUtils.java b/src/com/android/settings/search2/ResultPayloadUtils.java new file mode 100644 index 00000000000..41d15e22e49 --- /dev/null +++ b/src/com/android/settings/search2/ResultPayloadUtils.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 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.search2; + +import android.os.BadParcelableException; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.io.StreamCorruptedException; + +/** + * Utility class to Marshall and Unmarshall the payloads stored in the SQLite Database + */ +public class ResultPayloadUtils { + + private static final String TAG = "PayloadUtil"; + + public static byte[] marshall(ResultPayload payload) { + Parcel parcel = Parcel.obtain(); + payload.writeToParcel(parcel, 0); + byte[] bytes = parcel.marshall(); + parcel.recycle(); + return bytes; + } + + public static T unmarshall(byte[] bytes, Parcelable.Creator creator) { + T result; + Parcel parcel = unmarshall(bytes); + result = creator.createFromParcel(parcel); + parcel.recycle(); + return result; + } + + private static Parcel unmarshall(byte[] bytes) { + Parcel parcel = Parcel.obtain(); + parcel.unmarshall(bytes, 0, bytes.length); + parcel.setDataPosition(0); + return parcel; + } +} diff --git a/src/com/android/settings/search2/XMLParserUtil.java b/src/com/android/settings/search2/XmlParserUtils.java similarity index 75% rename from src/com/android/settings/search2/XMLParserUtil.java rename to src/com/android/settings/search2/XmlParserUtils.java index dcb3cbbb70a..748d4b0330f 100644 --- a/src/com/android/settings/search2/XMLParserUtil.java +++ b/src/com/android/settings/search2/XmlParserUtils.java @@ -30,19 +30,10 @@ import java.util.regex.Pattern; /** * Utility class to parse elements of XML preferences */ -public class XMLParserUtil { - - private static final String NON_BREAKING_HYPHEN = "\u2011"; - private static final String EMPTY = ""; - private static final String LIST_DELIMITERS = "[,]\\s*"; - private static final String HYPHEN = "-"; - private static final String SPACE = " "; +public class XmlParserUtils { private static final String ENTRIES_SEPARATOR = "|"; - private static final Pattern REMOVE_DIACRITICALS_PATTERN - = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); - public static String getDataKey(Context context, AttributeSet attrs) { return getData(context, attrs, com.android.internal.R.styleable.Preference, @@ -83,7 +74,7 @@ public class XMLParserUtil { return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_keywords); } - public static String getData(Context context, AttributeSet set, int[] attrs, int resId) { + private static String getData(Context context, AttributeSet set, int[] attrs, int resId) { final TypedArray sa = context.obtainStyledAttributes(set, attrs); final TypedValue tv = sa.peekValue(resId); @@ -98,7 +89,7 @@ public class XMLParserUtil { return (data != null) ? data.toString() : null; } - public static String getDataEntries(Context context, AttributeSet set, int[] attrs, int resId) { + private static String getDataEntries(Context context, AttributeSet set, int[] attrs, int resId) { final TypedArray sa = context.obtainStyledAttributes(set, attrs); final TypedValue tv = sa.peekValue(resId); @@ -119,19 +110,4 @@ public class XMLParserUtil { } return result.toString(); } - - public static String normalizeHyphen(String input) { - return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY; - } - - 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(); - } - - public static String normalizeKeywords(String input) { - return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY; - } } diff --git a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java new file mode 100644 index 00000000000..8b363b04897 --- /dev/null +++ b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java @@ -0,0 +1,616 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.settings.search; + +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.provider.SearchIndexableResource; +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.search2.DatabaseIndexingManager; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class DatabaseIndexingManagerTest { + private final String localeStr = "en_US"; + + private final int rank = 42; + private final String title = "title\u2011title"; + private final String updatedTitle = "title-title"; + private final String normalizedTitle = "titletitle"; + private final String summaryOn = "summary\u2011on"; + private final String updatedSummaryOn = "summary-on"; + private final String normalizedSummaryOn = "summaryon"; + private final String summaryOff = "summary\u2011off"; + private final String updatedSummaryOff ="summary-off"; + private final String normalizedSummaryOff = "summaryoff"; + private final String entries = "entries"; + private final String keywords = "keywords, keywordss, keywordsss"; + private final String spaceDelimittedKeywords = "keywords keywordss keywordsss"; + private final String screenTitle = "screen title"; + private final String className = "class name"; + private final int iconResId = 0xff; + private final String action = "action"; + private final String targetPackage = "target package"; + private final String targetClass = "target class"; + private final String packageName = "package name"; + private final String key = "key"; + private final int userId = -1; + private final boolean enabled = true; + + private Context mContext; + private DatabaseIndexingManager mManager; + private SQLiteDatabase mDb; + + @Before + public void setUp() { + mContext = ShadowApplication.getInstance().getApplicationContext(); + mManager = spy(new DatabaseIndexingManager(mContext, mContext.getPackageName())); + mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase(); + } + + @After + public void cleanUp() { + Field instance; + Class clazz = IndexDatabaseHelper.class; + try { + instance = clazz.getDeclaredField("sSingleton"); + instance.setAccessible(true); + instance.set(null, null); + } catch (Exception e) { + throw new RuntimeException(); + } + } + + @Test + public void testDatabaseSchema() { + Cursor dbCursor = mDb.query("prefs_index", null, null, null, null, null, null); + List columnNames = new ArrayList<>(Arrays.asList(dbCursor.getColumnNames())); + // Note that docid is not included. + List expColumnNames = new ArrayList<>(Arrays.asList(new String[ ]{ + "locale", + "data_rank", + "data_title", + "data_title_normalized", + "data_summary_on", + "data_summary_on_normalized", + "data_summary_off", + "data_summary_off_normalized", + "data_entries", + "data_keywords", + "class_name", + "screen_title", + "intent_action", + "intent_target_package", + "intent_target_class", + "icon", + "enabled", + "data_key_reference", + "user_id", + "payload_type", + "payload" + })); + // Prevent database schema regressions + assertThat(columnNames).containsAllIn(expColumnNames); + } + + // Tests for the flow: IndexOneRaw -> UpdateOneRowWithFilteredData -> UpdateOneRow + + @Test + public void testInsertRawColumn_RowInserted() { + SearchIndexableRaw raw = getFakeRaw(); + mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */); + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); + assertThat(cursor.getCount()).isEqualTo(1); + } + + @Test + public void testInsertRawColumn_RowMatches() { + SearchIndexableRaw raw = getFakeRaw(); + mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */); + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); + cursor.moveToPosition(0); + + // Locale + assertThat(cursor.getString(0)).isEqualTo(localeStr); + // Data Rank + assertThat(cursor.getInt(1)).isEqualTo(raw.rank); + // Data Title + assertThat(cursor.getString(2)).isEqualTo(updatedTitle); + // Normalized Title + assertThat(cursor.getString(3)).isEqualTo(normalizedTitle); + // Summary On + assertThat(cursor.getString(4)).isEqualTo(updatedSummaryOn); + // Summary On Normalized + assertThat(cursor.getString(5)).isEqualTo(normalizedSummaryOn); + // Summary Off + assertThat(cursor.getString(6)).isEqualTo(updatedSummaryOff); + // Summary off normalized + assertThat(cursor.getString(7)).isEqualTo(normalizedSummaryOff); + // Entries + assertThat(cursor.getString(8)).isEqualTo(raw.entries); + // Keywords + assertThat(cursor.getString(9)).isEqualTo(spaceDelimittedKeywords); + // Screen Title + assertThat(cursor.getString(10)).isEqualTo(raw.screenTitle); + // Class Name + assertThat(cursor.getString(11)).isEqualTo(raw.className); + // Icon + assertThat(cursor.getInt(12)).isEqualTo(raw.iconResId); + // Intent Action + assertThat(cursor.getString(13)).isEqualTo(raw.intentAction); + // Target Package + assertThat(cursor.getString(14)).isEqualTo(raw.intentTargetPackage); + // Target Class + assertThat(cursor.getString(15)).isEqualTo(raw.intentTargetClass); + // Enabled + assertThat(cursor.getInt(16) == 1).isEqualTo(raw.enabled); + // Data ref key + assertThat(cursor.getString(17)).isNotNull(); + // User Id + assertThat(cursor.getInt(18)).isEqualTo(raw.userId); + // Payload Type - default is 0 + assertThat(cursor.getInt(19)).isEqualTo(0); + // Payload + assertThat(cursor.getBlob(20)).isNull(); + } + + @Test + public void testInsertRawColumnMismatchedLocale_NoRowInserted() { + SearchIndexableRaw raw = getFakeRaw("ca-fr"); + mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */); + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); + assertThat(cursor.getCount()).isEqualTo(0); + } + + // Tests for the flow: IndexOneResource -> IndexFromResource -> + // UpdateOneRowWithFilteredData -> UpdateOneRow + + @Test + public void testNullResource_NothingInserted() { + mManager.indexOneSearchIndexableData(mDb, localeStr, null /* searchIndexableResource */, + new HashMap>()); + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); + assertThat(cursor.getCount()).isEqualTo(0); + } + + @Test + public void testAddResource_RowsInserted() { + SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings); + mManager.indexOneSearchIndexableData(mDb, localeStr, resource, + new HashMap>()); + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); + assertThat(cursor.getCount()).isEqualTo(6); + } + + @Test + public void testAddResourceHeader_RowsMatch() { + SearchIndexableResource resource = getFakeResource(R.xml.application_settings); + mManager.indexOneSearchIndexableData(mDb, localeStr, resource, + new HashMap>()); + + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null); + cursor.moveToPosition(1); + + // Locale + assertThat(cursor.getString(0)).isEqualTo(localeStr); + // Data Rank + assertThat(cursor.getInt(1)).isEqualTo(rank); + // Data Title + assertThat(cursor.getString(2)).isEqualTo("Apps"); + // Normalized Title + assertThat(cursor.getString(3)).isEqualTo("apps"); + // Summary On + assertThat(cursor.getString(4)).isEqualTo("Manage apps, set up quick launch shortcuts"); + // Summary On Normalized + assertThat(cursor.getString(5)).isEqualTo("manage apps, set up quick launch shortcuts"); + // Summary Off - only on for checkbox preferences + assertThat(cursor.getString(6)).isEmpty(); + // Summary off normalized - only on for checkbox preferences + assertThat(cursor.getString(7)).isEmpty(); + // Entries - only on for list preferences + assertThat(cursor.getString(8)).isNull(); + // Keywords + assertThat(cursor.getString(9)).isEmpty(); + // Screen Title + assertThat(cursor.getString(10)).isEqualTo("Apps"); + // Class Name + assertThat(cursor.getString(11)).isEqualTo(className); + // Icon + assertThat(cursor.getInt(12)).isEqualTo(iconResId); + // Intent Action + assertThat(cursor.getString(13)).isEqualTo(action); + // Target Package + assertThat(cursor.getString(14)).isEqualTo(targetPackage); + // Target Class + assertThat(cursor.getString(15)).isEqualTo(targetClass); + // Enabled + assertThat(cursor.getInt(16) == 1).isEqualTo(enabled); + // Data ref key + assertThat(cursor.getString(17)).isEqualTo("applications_settings"); + // User Id + assertThat(cursor.getInt(18)).isEqualTo(userId); + // Payload Type - default is 0 + assertThat(cursor.getInt(19)).isEqualTo(0); + // Payload - should be updated to real payloads as controllers are added + assertThat(cursor.getBlob(20)).isNull(); + } + + @Test + public void testAddResourceCustomSetting_RowsMatch() { + SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings); + mManager.indexOneSearchIndexableData(mDb, localeStr, resource, + new HashMap>()); + + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); + cursor.moveToPosition(0); + + // Locale + assertThat(cursor.getString(0)).isEqualTo(localeStr); + // Data Rank + assertThat(cursor.getInt(1)).isEqualTo(rank); + // Data Title + assertThat(cursor.getString(2)).isEqualTo("Swipe for notifications"); + // Normalized Title + assertThat(cursor.getString(3)).isEqualTo("swipe for notifications"); + // Summary On + assertThat(cursor.getString(4)).isEqualTo("To check your notifications, " + + "swipe down on the fingerprint sensor on the back of your phone."); + // Summary On Normalized + assertThat(cursor.getString(5)).isEqualTo("to check your notifications, " + + "swipe down on the fingerprint sensor on the back of your phone."); + // Summary Off - only on for checkbox preferences + assertThat(cursor.getString(6)).isEmpty(); + // Summary off normalized - only on for checkbox preferences + assertThat(cursor.getString(7)).isEmpty(); + // Entries - only on for list preferences + assertThat(cursor.getString(8)).isNull(); + // Keywords + assertThat(cursor.getString(9)).isEmpty(); + // Screen Title + assertThat(cursor.getString(10)).isEqualTo("Gestures"); + // Class Name + assertThat(cursor.getString(11)).isEqualTo(className); + // Icon + assertThat(cursor.getInt(12)).isEqualTo(iconResId); + // Intent Action + assertThat(cursor.getString(13)).isEqualTo(action); + // Target Package + assertThat(cursor.getString(14)).isEqualTo(targetPackage); + // Target Class + assertThat(cursor.getString(15)).isEqualTo(targetClass); + // Enabled + assertThat(cursor.getInt(16) == 1).isEqualTo(enabled); + // Data ref key + assertThat(cursor.getString(17)).isEqualTo("gesture_swipe_down_fingerprint"); + // User Id + assertThat(cursor.getInt(18)).isEqualTo(userId); + // Payload Type - default is 0 + assertThat(cursor.getInt(19)).isEqualTo(0); + // Payload - should be updated to real payloads as controllers are added + assertThat(cursor.getBlob(20)).isNull(); + } + + @Test + public void testAddResourceCheckboxPreference_RowsMatch() { + SearchIndexableResource resource = getFakeResource(R.xml.application_settings); + mManager.indexOneSearchIndexableData(mDb, localeStr, resource, + new HashMap>()); + + /* Should return 6 results, with the following titles: + * Advanced Settings, Apps, Manage Apps, Preferred install location, Running Services + */ + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null); + cursor.moveToPosition(0); + // Locale + assertThat(cursor.getString(0)).isEqualTo(localeStr); + // Data Rank + assertThat(cursor.getInt(1)).isEqualTo(rank); + // Data Title + assertThat(cursor.getString(2)).isEqualTo("Advanced settings"); + // Normalized Title + assertThat(cursor.getString(3)).isEqualTo("advanced settings"); + // Summary On + assertThat(cursor.getString(4)).isEqualTo("Enable more settings options"); + // Summary On Normalized + assertThat(cursor.getString(5)).isEqualTo("enable more settings options"); + // Summary Off + assertThat(cursor.getString(6)).isEqualTo("Enable more settings options"); + // Summary Off + assertThat(cursor.getString(7)).isEqualTo("enable more settings options"); + // Entries - only on for list preferences + assertThat(cursor.getString(8)).isNull(); + // Keywords + assertThat(cursor.getString(9)).isEmpty(); + // Screen Title + assertThat(cursor.getString(10)).isEqualTo("Apps"); + // Class Name + assertThat(cursor.getString(11)).isEqualTo(className); + // Icon + assertThat(cursor.getInt(12)).isEqualTo(iconResId); + // Intent Action + assertThat(cursor.getString(13)).isEqualTo(action); + // Target Package + assertThat(cursor.getString(14)).isEqualTo(targetPackage); + // Target Class + assertThat(cursor.getString(15)).isEqualTo(targetClass); + // Enabled + assertThat(cursor.getInt(16) == 1).isEqualTo(enabled); + // Data ref key + assertThat(cursor.getString(17)).isEqualTo("toggle_advanced_settings"); + // User Id + assertThat(cursor.getInt(18)).isEqualTo(userId); + // Payload Type - default is 0 + assertThat(cursor.getInt(19)).isEqualTo(0); + // Payload - should be updated to real payloads as controllers are added + assertThat(cursor.getBlob(20)).isNull(); + } + + @Test + public void testAddResourceListPreference_RowsMatch() { + SearchIndexableResource resource = getFakeResource(R.xml.application_settings); + mManager.indexOneSearchIndexableData(mDb, localeStr, resource, + new HashMap>()); + + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null); + cursor.moveToPosition(3); + // Locale + assertThat(cursor.getString(0)).isEqualTo(localeStr); + // Data Rank + assertThat(cursor.getInt(1)).isEqualTo(rank); + // Data Title + assertThat(cursor.getString(2)).isEqualTo("Preferred install location"); + // Normalized Title + assertThat(cursor.getString(3)).isEqualTo("preferred install location"); + // Summary On + assertThat(cursor.getString(4)).isEqualTo("Change the preferred installation location for new apps"); + // Summary On Normalized + assertThat(cursor.getString(5)).isEqualTo("change the preferred installation location for new apps"); + // Summary Off - only on for checkbox preferences + assertThat(cursor.getString(6)).isEmpty(); + // Summary off normalized - only on for checkbox preferences + assertThat(cursor.getString(7)).isEmpty(); + // Entries - only on for list preferences + assertThat(cursor.getString(8)).isEqualTo("Internal device storage|Removable SD card|" + + "Let the system decide|"); + // Keywords + assertThat(cursor.getString(9)).isEmpty(); + // Screen Title + assertThat(cursor.getString(10)).isEqualTo("Apps"); + // Class Name + assertThat(cursor.getString(11)).isEqualTo(className); + // Icon + assertThat(cursor.getInt(12)).isEqualTo(iconResId); + // Intent Action + assertThat(cursor.getString(13)).isEqualTo(action); + // Target Package + assertThat(cursor.getString(14)).isEqualTo(targetPackage); + // Target Class + assertThat(cursor.getString(15)).isEqualTo(targetClass); + // Enabled + assertThat(cursor.getInt(16) == 1).isEqualTo(enabled); + // Data ref key + assertThat(cursor.getString(17)).isEqualTo("app_install_location"); + // User Id + assertThat(cursor.getInt(18)).isEqualTo(userId); + // Payload Type - default is 0 + assertThat(cursor.getInt(19)).isEqualTo(0); + // Payload - should be updated to real payloads as controllers are added + assertThat(cursor.getBlob(20)).isNull(); + } + + // Tests for the flow: IndexOneResource -> IndexFromProvider -> IndexFromResource -> + // UpdateOneRowWithFilteredData -> UpdateOneRow + + @Test + public void testResourceProvider_RowInserted() { + SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings); + resource.xmlResId = 0; + resource.className = "com.android.settings.display.ScreenZoomSettings"; + + mManager.indexOneSearchIndexableData(mDb, localeStr, resource, + new HashMap>()); + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); + assertThat(cursor.getCount()).isEqualTo(1); + } + + @Test + public void testResourceProvider_Matches() { + SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings); + resource.xmlResId = 0; + resource.className = "com.android.settings.display.ScreenZoomSettings"; + + mManager.indexOneSearchIndexableData(mDb, localeStr, resource, + new HashMap>()); + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); + cursor.moveToPosition(0); + + // Locale + assertThat(cursor.getString(0)).isEqualTo(localeStr); + // Data Rank + assertThat(cursor.getInt(1)).isEqualTo(rank); + // Data Title + assertThat(cursor.getString(2)).isEqualTo("Display size"); + // Normalized Title + assertThat(cursor.getString(3)).isEqualTo("display size"); + // Summary On + assertThat(cursor.getString(4)).isEmpty(); + // Summary On Normalized + assertThat(cursor.getString(5)).isEmpty(); + // Summary Off - only on for checkbox preferences + assertThat(cursor.getString(6)).isEmpty(); + // Summary off normalized - only on for checkbox preferences + assertThat(cursor.getString(7)).isEmpty(); + // Entries - only on for list preferences + assertThat(cursor.getString(8)).isNull(); + // Keywords + assertThat(cursor.getString(9)).isEqualTo("display density screen zoom scale scaling"); + // Screen Title + assertThat(cursor.getString(10)).isEqualTo("Display size"); + // Class Name + assertThat(cursor.getString(11)) + .isEqualTo("com.android.settings.display.ScreenZoomSettings"); + // Icon + assertThat(cursor.getInt(12)).isEqualTo(iconResId); + // Intent Action + assertThat(cursor.getString(13)).isNull(); + // Target Package + assertThat(cursor.getString(14)).isNull(); + // Target Class + assertThat(cursor.getString(15)).isNull(); + // Enabled + assertThat(cursor.getInt(16) == 1).isEqualTo(enabled); + // Data ref key + assertThat(cursor.getString(17)).isNull(); + // User Id + assertThat(cursor.getInt(18)).isEqualTo(userId); + // Payload Type - default is 0 + assertThat(cursor.getInt(19)).isEqualTo(0); + // Payload - should be updated to real payloads as controllers are added + assertThat(cursor.getBlob(20)).isNull(); + } + + @Test + public void testResourceProvider_ResourceRowInserted() { + SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings); + resource.xmlResId = 0; + resource.className = "com.android.settings.LegalSettings"; + + mManager.indexOneSearchIndexableData(mDb, localeStr, resource, + new HashMap>()); + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); + assertThat(cursor.getCount()).isEqualTo(2); + } + + @Test + public void testResourceProvider_ResourceRowMatches() { + SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings); + resource.xmlResId = 0; + resource.className = "com.android.settings.LegalSettings"; + + mManager.indexOneSearchIndexableData(mDb, localeStr, resource, + new HashMap>()); + Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null); + cursor.moveToPosition(0); + + // Locale + assertThat(cursor.getString(0)).isEqualTo(localeStr); + // Data Rank + assertThat(cursor.getInt(1)).isEqualTo(rank); + // Data Title + assertThat(cursor.getString(2)).isEqualTo("Legal information"); + // Normalized Title + assertThat(cursor.getString(3)).isEqualTo("legal information"); + // Summary On + assertThat(cursor.getString(4)).isEmpty(); + // Summary On Normalized + assertThat(cursor.getString(5)).isEmpty(); + // Summary Off - only on for checkbox preferences + assertThat(cursor.getString(6)).isEmpty(); + // Summary off normalized - only on for checkbox preferences + assertThat(cursor.getString(7)).isEmpty(); + // Entries - only on for list preferences + assertThat(cursor.getString(8)).isNull(); + // Keywords + assertThat(cursor.getString(9)).isEmpty(); + // Screen Title + assertThat(cursor.getString(10)).isEqualTo("Legal information"); + // Class Name + assertThat(cursor.getString(11)) + .isEqualTo("com.android.settings.LegalSettings"); + // Icon + assertThat(cursor.getInt(12)).isEqualTo(iconResId); + // Intent Action + assertThat(cursor.getString(13)).isNull(); + // Target Package + assertThat(cursor.getString(14)).isNull(); + // Target Class + assertThat(cursor.getString(15)).isNull(); + // Enabled + assertThat(cursor.getInt(16) == 1).isEqualTo(enabled); + // Data ref key + assertThat(cursor.getString(17)).isNull(); + // User Id + assertThat(cursor.getInt(18)).isEqualTo(userId); + // Payload Type - default is 0 + assertThat(cursor.getInt(19)).isEqualTo(0); + // Payload - should be updated to real payloads as controllers are added + assertThat(cursor.getBlob(20)).isNull(); + } + + // Util functions + + private SearchIndexableRaw getFakeRaw() { + return getFakeRaw(localeStr); + } + + private SearchIndexableRaw getFakeRaw(String localeStr) { + SearchIndexableRaw data = new SearchIndexableRaw(mContext); + data.locale = new Locale(localeStr); + data.rank = rank; + data.title = title; + data.summaryOn = summaryOn; + data.summaryOff = summaryOff; + data.entries = entries; + data.keywords = keywords; + data.screenTitle = screenTitle; + data.className = className; + data.packageName = packageName; + data.iconResId = iconResId; + data.intentAction = action; + data.intentTargetPackage = targetPackage; + data.intentTargetClass = targetClass; + data.key = key; + data.userId = userId; + data.enabled = enabled; + return data; + } + + private SearchIndexableResource getFakeResource(int xml) { + SearchIndexableResource sir = new SearchIndexableResource(mContext); + sir.rank = rank; + sir.xmlResId = xml; + sir.className = className; + sir.packageName = packageName; + sir.iconResId = iconResId; + sir.intentAction = action; + sir.intentTargetPackage = targetPackage; + sir.intentTargetClass = targetClass; + sir.enabled = enabled; + return sir; + } +} diff --git a/tests/robotests/src/com/android/settings/search/DatabaseRowTest.java b/tests/robotests/src/com/android/settings/search/DatabaseRowTest.java new file mode 100644 index 00000000000..34626bd9f12 --- /dev/null +++ b/tests/robotests/src/com/android/settings/search/DatabaseRowTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2016 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 static for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.settings.search; + +import android.content.Intent; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.search2.DatabaseIndexingManager.DatabaseRow; +import com.android.settings.search2.DatabaseIndexingManager.DatabaseRow.Builder; +import com.android.settings.search2.IntentPayload; +import com.android.settings.search2.ResultPayload; +import com.android.settings.search2.ResultPayloadUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import static com.google.common.truth.Truth.assertThat; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class DatabaseRowTest { + private Builder builder; + + private static final String LOCALE = "locale"; + private static final String UPDATED_TITLE = "updated title"; + private static final String NORMALIZED_TITLE = "normal title"; + private static final String UPDATED_SUMMARY_ON = "updated summary on"; + private static final String NORMALIZED_SUMMARY_ON = "normalized summary on"; + private static final String UPDATED_SUMMARY_OFF = "updated summary off"; + private static final String NORMALIZED_SUMMARY_OFF = "normalized summary off"; + private static final String ENTRIES = "entries"; + private static final String CLASS_NAME = "class name"; + private static final String SCREEN_TITLE = "sceen title"; + private static final int ICON_RES_ID = 0xff; + private static final int RANK = 1; + private static final String SPACE_DELIMITED_KEYWORDS = "keywords"; + private static final String INTENT_ACTION = "intent action"; + private static final String INTENT_TARGET_PACKAGE = "target package"; + private static final String INTENT_TARGET_CLASS = "target class"; + private static final boolean ENABLED = true; + private static final String KEY = "key"; + private static final int USER_ID = 1; + private static IntentPayload intentPayload; + + private final String EXTRA_KEY = "key"; + private final String EXTRA_VALUE = "value"; + + @Before + public void setUp() { + Intent intent = new Intent(); + intent.putExtra(EXTRA_KEY, EXTRA_VALUE); + intentPayload = new IntentPayload(intent); + + builder = new DatabaseRow.Builder(); + } + + @Test + public void testFullRowBuild_NonNull() { + DatabaseRow row = generateRow(); + assertThat(row).isNotNull(); + } + + @Test + public void testPrimativesBuild_NoDataLoss() { + DatabaseRow row = generateRow(); + + assertThat(row.locale).isEqualTo(LOCALE); + assertThat(row.updatedTitle).isEqualTo(UPDATED_TITLE); + assertThat(row.normalizedTitle).isEqualTo(NORMALIZED_TITLE); + assertThat(row.updatedSummaryOn).isEqualTo(UPDATED_SUMMARY_ON); + assertThat(row.normalizedSummaryOn).isEqualTo(NORMALIZED_SUMMARY_ON); + assertThat(row.updatedSummaryOff).isEqualTo(UPDATED_SUMMARY_OFF); + assertThat(row.normalizedSummaryOff).isEqualTo(NORMALIZED_SUMMARY_OFF); + assertThat(row.entries).isEqualTo(ENTRIES); + assertThat(row.className).isEqualTo(CLASS_NAME); + assertThat(row.screenTitle).isEqualTo(SCREEN_TITLE); + assertThat(row.iconResId).isEqualTo(ICON_RES_ID); + assertThat(row.rank).isEqualTo(RANK); + assertThat(row.spaceDelimitedKeywords).isEqualTo(SPACE_DELIMITED_KEYWORDS); + assertThat(row.intentAction).isEqualTo(INTENT_ACTION); + assertThat(row.intentTargetClass).isEqualTo(INTENT_TARGET_CLASS); + assertThat(row.intentTargetPackage).isEqualTo(INTENT_TARGET_PACKAGE); + assertThat(row.enabled).isEqualTo(ENABLED); + assertThat(row.userId).isEqualTo(USER_ID); + assertThat(row.key).isEqualTo(KEY); + assertThat(row.payloadType).isEqualTo(ResultPayload.PayloadType.INTENT); + } + + @Test + public void testPayload_PayloadTypeAdded() { + DatabaseRow row = generateRow(); + byte[] marshalledPayload = row.payload; + IntentPayload payload = ResultPayloadUtils.unmarshall(marshalledPayload, + IntentPayload.CREATOR); + + Intent intent = payload.intent; + assertThat(intent.getExtra(EXTRA_KEY)).isEqualTo(EXTRA_VALUE); + } + + @Test + public void TestNullPayload_NoCrash() { + Builder builder = new Builder(); + builder.setPayload(null); + DatabaseRow row = builder.build(); + + assertThat(row.payload).isNull(); + } + + private DatabaseRow generateRow() { + builder.setLocale(LOCALE) + .setUpdatedTitle(UPDATED_TITLE) + .setNormalizedTitle(NORMALIZED_TITLE) + .setUpdatedSummaryOn(UPDATED_SUMMARY_ON) + .setNormalizedSummaryOn(NORMALIZED_SUMMARY_ON) + .setUpdatedSummaryOff(UPDATED_SUMMARY_OFF) + .setNormalizedSummaryOff(NORMALIZED_SUMMARY_OFF) + .setEntries(ENTRIES) + .setClassName(CLASS_NAME) + .setScreenTitle(SCREEN_TITLE) + .setIconResId(ICON_RES_ID) + .setRank(RANK) + .setSpaceDelimitedKeywords(SPACE_DELIMITED_KEYWORDS) + .setIntentAction(INTENT_ACTION) + .setIntentTargetPackage(INTENT_TARGET_PACKAGE) + .setIntentTargetClass(INTENT_TARGET_CLASS) + .setEnabled(ENABLED) + .setKey(KEY) + .setUserId(USER_ID) + .setPayload(intentPayload); + + return(builder.build()); + } +} +; diff --git a/tests/robotests/src/com/android/settings/search/ResultPayloadUtilsTest.java b/tests/robotests/src/com/android/settings/search/ResultPayloadUtilsTest.java new file mode 100644 index 00000000000..3875d869af6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/search/ResultPayloadUtilsTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.settings.search; + +import android.content.Intent; +import android.os.BadParcelableException; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.search2.IntentPayload; +import com.android.settings.search2.ResultPayloadUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import java.io.StreamCorruptedException; + +import static com.google.common.truth.Truth.assertThat; +import static junit.framework.Assert.fail; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ResultPayloadUtilsTest { + private IntentPayload payload; + + private final String EXTRA_KEY = "key"; + private final String EXTRA_VALUE = "value"; + + @Before + public void setUp() { + Intent intent = new Intent(); + intent.putExtra(EXTRA_KEY, EXTRA_VALUE); + payload = new IntentPayload(intent); + } + + @Test + public void testUnmarshallBadData_ExceptionThrown() { + byte[] badData = "I'm going to fail :)".getBytes(); + try { + ResultPayloadUtils.unmarshall(badData, IntentPayload.CREATOR); + fail("unmarshall should throw exception"); + } catch ( RuntimeException e) { + assertThat(e).isNotNull(); + } + } + + @Test + public void testMarshallIntentPayload_NonEmptyArray() { + byte[] marshalledPayload = ResultPayloadUtils.marshall(payload); + assertThat(marshalledPayload).isNotNull(); + assertThat(marshalledPayload).isNotEmpty(); + } + + @Test + public void testUnmarshall_PreservedData() { + byte[] marshalledPayload = ResultPayloadUtils.marshall(payload); + IntentPayload newPayload = ResultPayloadUtils.unmarshall(marshalledPayload, + IntentPayload.CREATOR); + + String originalIntentExtra = payload.intent.getStringExtra(EXTRA_KEY); + String copiedIntentExtra = newPayload.intent.getStringExtra(EXTRA_KEY); + assertThat(originalIntentExtra).isEqualTo(copiedIntentExtra); + } + +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java new file mode 100644 index 00000000000..d0c691c51ff --- /dev/null +++ b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.settings.search; + +import android.content.res.XmlResourceParser; +import android.content.Context; +import android.util.AttributeSet; +import android.util.Xml; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.R; + +import com.android.settings.search2.XmlParserUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; +import org.xmlpull.v1.XmlPullParser; + +import static com.google.common.truth.Truth.assertThat; + +/** + * These tests use a series of preferences that have specific attributes which are sometimes + * uncommon (such as summaryOn). + * + * If changing a preference file breaks a test in this test file, please replace its reference + * with another preference with a matchin replacement attribute. + */ +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class XmlParserUtilTest { + + private Context mContext; + + @Before + public void setUp() { + mContext = ShadowApplication.getInstance().getApplicationContext(); + } + + @Test + public void testDataTitleValid_ReturnsPreferenceTitle() { + XmlResourceParser parser = getChildByType(R.xml.gesture_settings, + "com.android.settings.gestures.GesturePreference"); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String title = XmlParserUtils.getDataTitle(mContext, attrs); + String expTitle = mContext.getString(R.string.fingerprint_swipe_for_notifications_title); + assertThat(title).isEqualTo(expTitle); + } + + @Test + public void testDataKeywordsValid_ReturnsPreferenceKeywords() { + XmlResourceParser parser = getParentPrimedParser(R.xml.gesture_settings); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String keywords = XmlParserUtils.getDataKeywords(mContext, attrs); + String expKeywords = mContext.getString(R.string.keywords_gesture); + assertThat(keywords).isEqualTo(expKeywords); + } + + @Test + public void testDataKeyValid_ReturnsPreferenceKey() { + XmlResourceParser parser = getChildByType(R.xml.gesture_settings, + "com.android.settings.gestures.GesturePreference"); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String key = XmlParserUtils.getDataKey(mContext, attrs); + String expKey = "gesture_swipe_down_fingerprint"; + assertThat(key).isEqualTo(expKey); + } + + @Test + public void testDataSummaryValid_ReturnsPreferenceSummary() { + XmlResourceParser parser = getChildByType(R.xml.gesture_settings, + "com.android.settings.gestures.GesturePreference"); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String summary = XmlParserUtils.getDataSummary(mContext, attrs); + String expSummary = mContext.getString(R.string.fingerprint_swipe_for_notifications_summary); + assertThat(summary).isEqualTo(expSummary); + + } + + @Test + public void testDataSummaryOnValid_ReturnsPreferenceSummaryOn() { + XmlResourceParser parser = getChildByType(R.xml.application_settings, "CheckBoxPreference"); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String summary = XmlParserUtils.getDataSummaryOn(mContext, attrs); + String expSummary = mContext.getString(R.string.advanced_settings_summary); + assertThat(summary).isEqualTo(expSummary); + } + + @Test + public void testDataSummaryOffValid_ReturnsPreferenceSummaryOff() { + XmlResourceParser parser = getChildByType(R.xml.application_settings, "CheckBoxPreference"); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String summary = XmlParserUtils.getDataSummaryOff(mContext, attrs); + String expSummary = mContext.getString(R.string.advanced_settings_summary); + assertThat(summary).isEqualTo(expSummary); + } + + @Test + public void testDataEntriesValid_ReturnsPreferenceEntries() { + XmlResourceParser parser = getChildByType(R.xml.application_settings, "ListPreference"); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String entries = XmlParserUtils.getDataEntries(mContext, attrs); + String[] expEntries = mContext.getResources() + .getStringArray(R.array.app_install_location_entries); + for (int i = 0; i < expEntries.length; i++) { + assertThat(entries).contains(expEntries[i]); + } + } + + // Null checks + + @Test + public void testDataKeyInvalid_ReturnsNull() { + XmlResourceParser parser = getParentPrimedParser(R.xml.gesture_settings); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String key = XmlParserUtils.getDataKey(mContext, attrs); + assertThat(key).isNull(); + } + + @Test + public void testDataSummaryInvalid_ReturnsNull() { + XmlResourceParser parser = getParentPrimedParser(R.xml.gesture_settings); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String summary = XmlParserUtils.getDataSummary(mContext, attrs); + assertThat(summary).isNull(); + } + + @Test + public void testDataSummaryOffInvalid_ReturnsNull() { + XmlResourceParser parser = getParentPrimedParser(R.xml.gesture_settings); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String summaryOff = XmlParserUtils.getDataSummaryOff(mContext, attrs); + assertThat(summaryOff).isNull(); + } + + @Test + public void testDataEntriesInvalid_ReturnsNull() { + XmlResourceParser parser = getParentPrimedParser(R.xml.gesture_settings); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String entries = XmlParserUtils.getDataEntries(mContext, attrs); + assertThat(entries).isNull(); + } + + /** + * @param resId the ID for the XML preference + * @return an XML resource parser that points to the start tag + */ + private XmlResourceParser getParentPrimedParser(int resId) { + XmlResourceParser parser = null; + try { + parser = mContext.getResources().getXml(resId); + + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + } + } catch (Exception e) { + + } + return parser; + } + + private XmlResourceParser getChildByType(int resId, String xmlType) { + XmlResourceParser parser = null; + try { + parser = mContext.getResources().getXml(resId); + + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + } + while(parser.getName() != xmlType) { + parser.next(); + } + } catch (Exception e) { + + } + return parser; + } +}