SearchFragment removes stale data from the database
When search fragment is created, it will get a list of the non-indexable keys and verify that each of the results are not a part of that list. This CL moves the non-indexable keys logic into a controller for shared use between SearchFragment and DatabaseIndexingManager. Bug:33209418 Test: make RunSettingsRoboTests Change-Id: I4ed3812ecc5ee9e63b75ba6edbc7ff8712e8e9c9
This commit is contained in:
@@ -27,7 +27,6 @@ import android.content.res.XmlResourceParser;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
import android.database.sqlite.SQLiteFullException;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.provider.SearchIndexableData;
|
import android.provider.SearchIndexableData;
|
||||||
@@ -53,10 +52,12 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
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_NON_INDEXABLE_KEYS_KEY_VALUE;
|
||||||
@@ -82,9 +83,40 @@ import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INT
|
|||||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK;
|
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_RESID;
|
||||||
|
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.CLASS_NAME;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_ENTRIES;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEY_REF;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_RANK;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DOCID;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ENABLED;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ICON;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_ACTION;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.LOCALE;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.PAYLOAD;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.SCREEN_TITLE;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.USER_ID;
|
||||||
|
import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX;
|
||||||
|
|
||||||
|
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_ID;
|
||||||
|
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
|
||||||
|
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_KEY;
|
||||||
|
import static com.android.settings.search2.DatabaseResultLoader.SELECT_COLUMNS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consumes the SearchIndexableProvider content providers.
|
* Consumes the SearchIndexableProvider content providers.
|
||||||
* Updates the Resource, Raw Data and non-indexable data for Search.
|
* Updates the Resource, Raw Data and non-indexable data for Search.
|
||||||
|
*
|
||||||
|
* TODO this class needs to be refactored by moving most of its methods into controllers
|
||||||
*/
|
*/
|
||||||
public class DatabaseIndexingManager {
|
public class DatabaseIndexingManager {
|
||||||
private static final String LOG_TAG = "DatabaseIndexingManager";
|
private static final String LOG_TAG = "DatabaseIndexingManager";
|
||||||
@@ -93,51 +125,14 @@ public class DatabaseIndexingManager {
|
|||||||
private static final String NODE_NAME_CHECK_BOX_PREFERENCE = "CheckBoxPreference";
|
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_LIST_PREFERENCE = "ListPreference";
|
||||||
|
|
||||||
private static final List<String> EMPTY_LIST = Collections.<String>emptyList();
|
private static final List<String> EMPTY_LIST = Collections.emptyList();
|
||||||
|
|
||||||
private final String mBaseAuthority;
|
private final String mBaseAuthority;
|
||||||
|
|
||||||
/**
|
|
||||||
* A private class to describe the update data for the Index database
|
|
||||||
*/
|
|
||||||
private static class UpdateData {
|
|
||||||
public List<SearchIndexableData> dataToUpdate;
|
|
||||||
public List<SearchIndexableData> dataToDelete;
|
|
||||||
public Map<String, List<String>> nonIndexableKeys;
|
|
||||||
|
|
||||||
public boolean forceUpdate;
|
|
||||||
public boolean fullIndex;
|
|
||||||
|
|
||||||
public UpdateData() {
|
|
||||||
dataToUpdate = new ArrayList<>();
|
|
||||||
dataToDelete = new ArrayList<>();
|
|
||||||
nonIndexableKeys = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public UpdateData(DatabaseIndexingManager.UpdateData other) {
|
|
||||||
dataToUpdate = new ArrayList<>(other.dataToUpdate);
|
|
||||||
dataToDelete = new ArrayList<>(other.dataToDelete);
|
|
||||||
nonIndexableKeys = new HashMap<>(other.nonIndexableKeys);
|
|
||||||
forceUpdate = other.forceUpdate;
|
|
||||||
fullIndex = other.fullIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DatabaseIndexingManager.UpdateData copy() {
|
|
||||||
return new DatabaseIndexingManager.UpdateData(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
dataToUpdate.clear();
|
|
||||||
dataToDelete.clear();
|
|
||||||
nonIndexableKeys.clear();
|
|
||||||
forceUpdate = false;
|
|
||||||
fullIndex = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final AtomicBoolean mIsAvailable = new AtomicBoolean(false);
|
private final AtomicBoolean mIsAvailable = new AtomicBoolean(false);
|
||||||
private final DatabaseIndexingManager.UpdateData mDataToProcess =
|
|
||||||
new DatabaseIndexingManager.UpdateData();
|
@VisibleForTesting
|
||||||
|
final UpdateData mDataToProcess = new UpdateData();
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
|
||||||
public DatabaseIndexingManager(Context context, String baseAuthority) {
|
public DatabaseIndexingManager(Context context, String baseAuthority) {
|
||||||
@@ -157,31 +152,201 @@ public class DatabaseIndexingManager {
|
|||||||
AsyncTask.execute(new Runnable() {
|
AsyncTask.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
|
performIndexing();
|
||||||
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 (!DatabaseIndexingUtils.isWellKnownProvider(info, mContext)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final String authority = info.providerInfo.authority;
|
|
||||||
final String packageName = info.providerInfo.packageName;
|
|
||||||
|
|
||||||
addIndexablesFromRemoteProvider(packageName, authority);
|
|
||||||
addNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
|
||||||
}
|
|
||||||
|
|
||||||
mDataToProcess.fullIndex = true;
|
|
||||||
updateInternal();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
|
/**
|
||||||
|
* Accumulate all data and non-indexable keys from each of the content-providers.
|
||||||
|
* Only the first indexing for the default language gets static search results - subsequent
|
||||||
|
* calls will only gather non-indexable keys.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void performIndexing() {
|
||||||
|
final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
|
||||||
|
final List<ResolveInfo> list =
|
||||||
|
mContext.getPackageManager().queryIntentContentProviders(intent, 0);
|
||||||
|
|
||||||
|
final boolean isLocaleIndexed = isLocaleIndexed();
|
||||||
|
|
||||||
|
for (final ResolveInfo info : list) {
|
||||||
|
if (!DatabaseIndexingUtils.isWellKnownProvider(info, mContext)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final String authority = info.providerInfo.authority;
|
||||||
|
final String packageName = info.providerInfo.packageName;
|
||||||
|
|
||||||
|
if (!isLocaleIndexed) {
|
||||||
|
addIndexablesFromRemoteProvider(packageName, authority);
|
||||||
|
}
|
||||||
|
addNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String localeStr = Locale.getDefault().toString();
|
||||||
|
updateDatabase(isLocaleIndexed, localeStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean isLocaleIndexed() {
|
||||||
|
final String locale = Locale.getDefault().toString();
|
||||||
|
return IndexDatabaseHelper.getInstance(mContext).isLocaleAlreadyIndexed(mContext, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds new data to the database and verifies the correctness of the ENABLED column.
|
||||||
|
* First, the data to be updated and all non-indexable keys are copied locally.
|
||||||
|
* Then all new data to be added is inserted.
|
||||||
|
* Then search results are verified to have the correct value of enabled.
|
||||||
|
* Finally, we record that the locale has been indexed.
|
||||||
|
*
|
||||||
|
* @param isIncrementalUpdate true when the language has already been indexed.
|
||||||
|
* @param localeStr the default locale for the device.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void updateDatabase(boolean isIncrementalUpdate, String localeStr) {
|
||||||
|
mIsAvailable.set(false);
|
||||||
|
final UpdateData copy;
|
||||||
|
|
||||||
|
synchronized (mDataToProcess) {
|
||||||
|
copy = mDataToProcess.copy();
|
||||||
|
mDataToProcess.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<SearchIndexableData> dataToUpdate = copy.dataToUpdate;
|
||||||
|
final Map<String, Set<String>> nonIndexableKeys = copy.nonIndexableKeys;
|
||||||
|
|
||||||
|
final SQLiteDatabase database = getWritableDatabase();
|
||||||
|
if (database == null) {
|
||||||
|
Log.w(LOG_TAG, "Cannot indexDatabase Index as I cannot get a writable database");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
database.beginTransaction();
|
||||||
|
|
||||||
|
// Add new data from Providers at initial index time, or inserted later.
|
||||||
|
if (dataToUpdate.size() > 0) {
|
||||||
|
addDataToDatabase(database, localeStr, dataToUpdate, nonIndexableKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only check for non-indexable key updates after initial index.
|
||||||
|
// Enabled state with non-indexable keys is checked when items are first inserted.
|
||||||
|
if (isIncrementalUpdate) {
|
||||||
|
updateDataInDatabase(database, nonIndexableKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
database.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
database.endTransaction();
|
||||||
|
}
|
||||||
|
// TODO Refactor: move the locale out of the helper class
|
||||||
|
IndexDatabaseHelper.setLocaleIndexed(mContext, localeStr);
|
||||||
|
|
||||||
|
mIsAvailable.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts {@link SearchIndexableData} into the database.
|
||||||
|
*
|
||||||
|
* @param database where the data will be inserted.
|
||||||
|
* @param localeStr is the locale of the data to be inserted.
|
||||||
|
* @param dataToUpdate is a {@link List} of the data to be inserted.
|
||||||
|
* @param nonIndexableKeys is a {@link Map} from Package Name to a {@link Set} of keys which
|
||||||
|
* identify search results which should not be surfaced.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void addDataToDatabase(SQLiteDatabase database, String localeStr,
|
||||||
|
List<SearchIndexableData> dataToUpdate, Map<String, Set<String>> nonIndexableKeys) {
|
||||||
|
final long current = System.currentTimeMillis();
|
||||||
|
|
||||||
|
for (SearchIndexableData data : dataToUpdate) {
|
||||||
|
try {
|
||||||
|
indexOneSearchIndexableData(database, localeStr, data, nonIndexableKeys);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(LOG_TAG, "Cannot index: " + (data != null ? data.className : data)
|
||||||
|
+ " for locale: " + localeStr, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final long now = System.currentTimeMillis();
|
||||||
|
Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
|
||||||
|
(now - current) + " millis");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upholds the validity of enabled data for the user.
|
||||||
|
* All rows which are enabled but are now flagged with non-indexable keys will become disabled.
|
||||||
|
* All rows which are disabled but no longer a non-indexable key will become enabled.
|
||||||
|
*
|
||||||
|
* @param database The database to validate.
|
||||||
|
* @param nonIndexableKeys A map between package name and the set of non-indexable keys for it.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||||
|
void updateDataInDatabase(SQLiteDatabase database,
|
||||||
|
Map<String, Set<String>> nonIndexableKeys) {
|
||||||
|
final String whereEnabled = ENABLED + " = 1";
|
||||||
|
final String whereDisabled = ENABLED + " = 0";
|
||||||
|
|
||||||
|
final Cursor enabledResults = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS,
|
||||||
|
whereEnabled, null, null, null, null);
|
||||||
|
|
||||||
|
final ContentValues enabledToDisabledValue = new ContentValues();
|
||||||
|
enabledToDisabledValue.put(ENABLED, 0);
|
||||||
|
|
||||||
|
String packageName;
|
||||||
|
// TODO Refactor: Move these two loops into one method.
|
||||||
|
while (enabledResults.moveToNext()) {
|
||||||
|
// Package name is the key for remote providers.
|
||||||
|
// If package name is null, the provider is Settings.
|
||||||
|
packageName = enabledResults.getString(COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
|
||||||
|
if (packageName == null) {
|
||||||
|
packageName = mContext.getPackageName();
|
||||||
|
}
|
||||||
|
|
||||||
|
final String key = enabledResults.getString(COLUMN_INDEX_KEY);
|
||||||
|
final Set<String> packageKeys = nonIndexableKeys.get(packageName);
|
||||||
|
|
||||||
|
// The indexed item is set to Enabled but is now non-indexable
|
||||||
|
if (packageKeys != null && packageKeys.contains(key)) {
|
||||||
|
final String whereClause = DOCID + " = " + enabledResults.getInt(COLUMN_INDEX_ID);
|
||||||
|
database.update(TABLE_PREFS_INDEX, enabledToDisabledValue, whereClause, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enabledResults.close();
|
||||||
|
|
||||||
|
final Cursor disabledResults = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS,
|
||||||
|
whereDisabled, null, null, null, null);
|
||||||
|
|
||||||
|
final ContentValues disabledToEnabledValue = new ContentValues();
|
||||||
|
disabledToEnabledValue.put(ENABLED, 1);
|
||||||
|
|
||||||
|
while (disabledResults.moveToNext()) {
|
||||||
|
// Package name is the key for remote providers.
|
||||||
|
// If package name is null, the provider is Settings.
|
||||||
|
packageName = disabledResults.getString(COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
|
||||||
|
if (packageName == null) {
|
||||||
|
packageName = mContext.getPackageName();
|
||||||
|
}
|
||||||
|
|
||||||
|
final String key = disabledResults.getString(COLUMN_INDEX_KEY);
|
||||||
|
final Set<String> packageKeys = nonIndexableKeys.get(packageName);
|
||||||
|
|
||||||
|
// The indexed item is set to Disabled but is no longer non-indexable.
|
||||||
|
// We do not enable keys when packageKeys is null because it means the keys came
|
||||||
|
// from an unrecognized package and therefore should not be surfaced as results.
|
||||||
|
if (packageKeys != null && !packageKeys.contains(key)) {
|
||||||
|
String whereClause = DOCID + " = " + disabledResults.getInt(COLUMN_INDEX_ID);
|
||||||
|
database.update(TABLE_PREFS_INDEX, disabledToEnabledValue, whereClause, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disabledResults.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
|
||||||
|
try {
|
||||||
|
// TODO delete base rank. does nothing.
|
||||||
final int baseRank = Ranking.getBaseRankForAuthority(authority);
|
final int baseRank = Ranking.getBaseRankForAuthority(authority);
|
||||||
|
|
||||||
final Context context = mBaseAuthority.equals(authority) ?
|
final Context context = mBaseAuthority.equals(authority) ?
|
||||||
@@ -202,11 +367,12 @@ public class DatabaseIndexingManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addNonIndexablesKeysFromRemoteProvider(String packageName,
|
@VisibleForTesting
|
||||||
|
void addNonIndexablesKeysFromRemoteProvider(String packageName,
|
||||||
String authority) {
|
String authority) {
|
||||||
final List<String> keys =
|
final List<String> keys =
|
||||||
getNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
getNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
||||||
addNonIndexableKeys(packageName, keys);
|
addNonIndexableKeys(packageName, new HashSet<>(keys));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getNonIndexablesKeysFromRemoteProvider(String packageName,
|
private List<String> getNonIndexablesKeysFromRemoteProvider(String packageName,
|
||||||
@@ -235,7 +401,7 @@ public class DatabaseIndexingManager {
|
|||||||
return EMPTY_LIST;
|
return EMPTY_LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> result = new ArrayList<String>();
|
final List<String> result = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
final int count = cursor.getCount();
|
final int count = cursor.getCount();
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
@@ -256,31 +422,19 @@ public class DatabaseIndexingManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteIndexableData(SearchIndexableData data) {
|
public void addNonIndexableKeys(String authority, Set<String> keys) {
|
||||||
synchronized (mDataToProcess) {
|
|
||||||
mDataToProcess.dataToDelete.add(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addNonIndexableKeys(String authority, List<String> keys) {
|
|
||||||
synchronized (mDataToProcess) {
|
synchronized (mDataToProcess) {
|
||||||
mDataToProcess.nonIndexableKeys.put(authority, keys);
|
mDataToProcess.nonIndexableKeys.put(authority, keys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFromRemoteProvider(String packageName, String authority) {
|
|
||||||
if (addIndexablesFromRemoteProvider(packageName, authority)) {
|
|
||||||
updateInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the Index for a specific class name resources
|
* Update the Index for a specific class name resources
|
||||||
*
|
*
|
||||||
* @param className the class name (typically a fragment name).
|
* @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 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
|
* @param includeInSearchResults true means that you want the bit "enabled" set so that the
|
||||||
* data will be seen included into the search results
|
* data will be seen included into the search results
|
||||||
*/
|
*/
|
||||||
public void updateFromClassNameResource(String className, final boolean rebuild,
|
public void updateFromClassNameResource(String className, final boolean rebuild,
|
||||||
boolean includeInSearchResults) {
|
boolean includeInSearchResults) {
|
||||||
@@ -297,12 +451,8 @@ public class DatabaseIndexingManager {
|
|||||||
AsyncTask.execute(new Runnable() {
|
AsyncTask.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (rebuild) {
|
|
||||||
deleteIndexableData(res);
|
|
||||||
}
|
|
||||||
addIndexableData(res);
|
addIndexableData(res);
|
||||||
mDataToProcess.forceUpdate = true;
|
performIndexing();
|
||||||
updateInternal();
|
|
||||||
res.enabled = false;
|
res.enabled = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -313,8 +463,7 @@ public class DatabaseIndexingManager {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
addIndexableData(data);
|
addIndexableData(data);
|
||||||
mDataToProcess.forceUpdate = true;
|
performIndexing();
|
||||||
updateInternal();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -347,16 +496,6 @@ public class DatabaseIndexingManager {
|
|||||||
SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
|
SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateInternal() {
|
|
||||||
synchronized (mDataToProcess) {
|
|
||||||
final DatabaseIndexingManager.UpdateIndexTask task =
|
|
||||||
new DatabaseIndexingManager.UpdateIndexTask();
|
|
||||||
DatabaseIndexingManager.UpdateData copy = mDataToProcess.copy();
|
|
||||||
task.execute(copy);
|
|
||||||
mDataToProcess.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addIndexablesForXmlResourceUri(Context packageContext, String packageName,
|
private void addIndexablesForXmlResourceUri(Context packageContext, String packageName,
|
||||||
Uri uri, String[] projection, int baseRank) {
|
Uri uri, String[] projection, int baseRank) {
|
||||||
|
|
||||||
@@ -468,7 +607,7 @@ public class DatabaseIndexingManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
|
public void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
|
||||||
SearchIndexableData data, Map<String, List<String>> nonIndexableKeys) {
|
SearchIndexableData data, Map<String, Set<String>> nonIndexableKeys) {
|
||||||
if (data instanceof SearchIndexableResource) {
|
if (data instanceof SearchIndexableResource) {
|
||||||
indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);
|
indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);
|
||||||
} else if (data instanceof SearchIndexableRaw) {
|
} else if (data instanceof SearchIndexableRaw) {
|
||||||
@@ -502,7 +641,7 @@ public class DatabaseIndexingManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void indexOneResource(SQLiteDatabase database, String localeStr,
|
private void indexOneResource(SQLiteDatabase database, String localeStr,
|
||||||
SearchIndexableResource sir, Map<String, List<String>> nonIndexableKeysFromResource) {
|
SearchIndexableResource sir, Map<String, Set<String>> nonIndexableKeysFromResource) {
|
||||||
|
|
||||||
if (sir == null) {
|
if (sir == null) {
|
||||||
Log.e(LOG_TAG, "Cannot index a null resource!");
|
Log.e(LOG_TAG, "Cannot index a null resource!");
|
||||||
@@ -512,9 +651,9 @@ public class DatabaseIndexingManager {
|
|||||||
final List<String> nonIndexableKeys = new ArrayList<String>();
|
final List<String> nonIndexableKeys = new ArrayList<String>();
|
||||||
|
|
||||||
if (sir.xmlResId > SearchIndexableResources.NO_DATA_RES_ID) {
|
if (sir.xmlResId > SearchIndexableResources.NO_DATA_RES_ID) {
|
||||||
List<String> resNonIndxableKeys = nonIndexableKeysFromResource.get(sir.packageName);
|
Set<String> resNonIndexableKeys = nonIndexableKeysFromResource.get(sir.packageName);
|
||||||
if (resNonIndxableKeys != null && resNonIndxableKeys.size() > 0) {
|
if (resNonIndexableKeys != null && resNonIndexableKeys.size() > 0) {
|
||||||
nonIndexableKeys.addAll(resNonIndxableKeys);
|
nonIndexableKeys.addAll(resNonIndexableKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
indexFromResource(database, localeStr, sir, nonIndexableKeys);
|
indexFromResource(database, localeStr, sir, nonIndexableKeys);
|
||||||
@@ -605,6 +744,7 @@ public class DatabaseIndexingManager {
|
|||||||
headerKeywords = XmlParserUtils.getDataKeywords(context, attrs);
|
headerKeywords = XmlParserUtils.getDataKeywords(context, attrs);
|
||||||
enabled = !nonIndexableKeys.contains(key);
|
enabled = !nonIndexableKeys.contains(key);
|
||||||
|
|
||||||
|
// TODO: Set payload type for header results
|
||||||
DatabaseRow.Builder headerBuilder = new DatabaseRow.Builder();
|
DatabaseRow.Builder headerBuilder = new DatabaseRow.Builder();
|
||||||
headerBuilder.setLocale(localeStr)
|
headerBuilder.setLocale(localeStr)
|
||||||
.setEntries(null)
|
.setEntries(null)
|
||||||
@@ -799,31 +939,29 @@ public class DatabaseIndexingManager {
|
|||||||
|
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DOCID, row.getDocId());
|
values.put(IndexDatabaseHelper.IndexColumns.DOCID, row.getDocId());
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, row.locale);
|
values.put(LOCALE, row.locale);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, row.rank);
|
values.put(DATA_RANK, row.rank);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, row.updatedTitle);
|
values.put(DATA_TITLE, row.updatedTitle);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, row.normalizedTitle);
|
values.put(DATA_TITLE_NORMALIZED, row.normalizedTitle);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, row.updatedSummaryOn);
|
values.put(DATA_SUMMARY_ON, row.updatedSummaryOn);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED,
|
values.put(DATA_SUMMARY_ON_NORMALIZED, row.normalizedSummaryOn);
|
||||||
row.normalizedSummaryOn);
|
values.put(DATA_SUMMARY_OFF, row.updatedSummaryOff);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, row.updatedSummaryOff);
|
values.put(DATA_SUMMARY_OFF_NORMALIZED, row.normalizedSummaryOff);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED,
|
values.put(DATA_ENTRIES, row.entries);
|
||||||
row.normalizedSummaryOff);
|
values.put(DATA_KEYWORDS, row.spaceDelimitedKeywords);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, row.entries);
|
values.put(CLASS_NAME, row.className);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, row.spaceDelimitedKeywords);
|
values.put(SCREEN_TITLE, row.screenTitle);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME, row.className);
|
values.put(INTENT_ACTION, row.intentAction);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, row.screenTitle);
|
values.put(INTENT_TARGET_PACKAGE, row.intentTargetPackage);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, row.intentAction);
|
values.put(INTENT_TARGET_CLASS, row.intentTargetClass);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, row.intentTargetPackage);
|
values.put(ICON, row.iconResId);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, row.intentTargetClass);
|
values.put(ENABLED, row.enabled);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.ICON, row.iconResId);
|
values.put(DATA_KEY_REF, row.key);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.ENABLED, row.enabled);
|
values.put(USER_ID, row.userId);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, row.key);
|
values.put(PAYLOAD_TYPE, row.payloadType);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.USER_ID, row.userId);
|
values.put(PAYLOAD, row.payload);
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, row.payloadType);
|
|
||||||
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, row.payload);
|
|
||||||
|
|
||||||
database.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
|
database.replaceOrThrow(TABLE_PREFS_INDEX, null, values);
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(row.className) && !TextUtils.isEmpty(row.childClassName)) {
|
if (!TextUtils.isEmpty(row.className) && !TextUtils.isEmpty(row.childClassName)) {
|
||||||
ContentValues siteMapPair = new ContentValues();
|
ContentValues siteMapPair = new ContentValues();
|
||||||
@@ -839,129 +977,34 @@ public class DatabaseIndexingManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A private class for updating the Index database
|
* A private class to describe the indexDatabase data for the Index database
|
||||||
*/
|
*/
|
||||||
private class UpdateIndexTask extends AsyncTask<DatabaseIndexingManager.UpdateData, Integer,
|
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||||
Void> {
|
static class UpdateData {
|
||||||
|
public List<SearchIndexableData> dataToUpdate;
|
||||||
|
public List<SearchIndexableData> dataToDisable;
|
||||||
|
public Map<String, Set<String>> nonIndexableKeys;
|
||||||
|
|
||||||
@Override
|
public UpdateData() {
|
||||||
protected void onPreExecute() {
|
dataToUpdate = new ArrayList<>();
|
||||||
super.onPreExecute();
|
dataToDisable = new ArrayList<>();
|
||||||
mIsAvailable.set(false);
|
nonIndexableKeys = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public UpdateData(UpdateData other) {
|
||||||
protected void onPostExecute(Void aVoid) {
|
dataToUpdate = new ArrayList<>(other.dataToUpdate);
|
||||||
super.onPostExecute(aVoid);
|
dataToDisable = new ArrayList<>(other.dataToDisable);
|
||||||
mIsAvailable.set(true);
|
nonIndexableKeys = new HashMap<>(other.nonIndexableKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public UpdateData copy() {
|
||||||
protected Void doInBackground(DatabaseIndexingManager.UpdateData... params) {
|
return new UpdateData(this);
|
||||||
try {
|
|
||||||
final List<SearchIndexableData> dataToUpdate = params[0].dataToUpdate;
|
|
||||||
final List<SearchIndexableData> dataToDelete = params[0].dataToDelete;
|
|
||||||
final Map<String, List<String>> nonIndexableKeys = params[0].nonIndexableKeys;
|
|
||||||
|
|
||||||
final boolean forceUpdate = params[0].forceUpdate;
|
|
||||||
final boolean fullIndex = params[0].fullIndex;
|
|
||||||
|
|
||||||
final SQLiteDatabase database = getWritableDatabase();
|
|
||||||
if (database == null) {
|
|
||||||
Log.e(LOG_TAG, "Cannot update Index as I cannot get a writable database");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final String localeStr = Locale.getDefault().toString();
|
|
||||||
|
|
||||||
try {
|
|
||||||
database.beginTransaction();
|
|
||||||
if (dataToDelete.size() > 0) {
|
|
||||||
processDataToDelete(database, localeStr, dataToDelete);
|
|
||||||
}
|
|
||||||
if (dataToUpdate.size() > 0) {
|
|
||||||
processDataToUpdate(database, localeStr, dataToUpdate, nonIndexableKeys,
|
|
||||||
forceUpdate);
|
|
||||||
}
|
|
||||||
database.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
database.endTransaction();
|
|
||||||
}
|
|
||||||
if (fullIndex) {
|
|
||||||
IndexDatabaseHelper.setLocaleIndexed(mContext, localeStr);
|
|
||||||
}
|
|
||||||
} catch (SQLiteFullException e) {
|
|
||||||
Log.e(LOG_TAG, "Unable to index search, out of space", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processDataToUpdate(SQLiteDatabase database, String localeStr,
|
public void clear() {
|
||||||
List<SearchIndexableData> dataToUpdate, Map<String, List<String>> nonIndexableKeys,
|
dataToUpdate.clear();
|
||||||
boolean forceUpdate) {
|
dataToDisable.clear();
|
||||||
|
nonIndexableKeys.clear();
|
||||||
if (!forceUpdate && IndexDatabaseHelper.isLocaleAlreadyIndexed(mContext, localeStr)) {
|
|
||||||
Log.d(LOG_TAG, "Locale '" + localeStr + "' is already indexed");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean result = false;
|
|
||||||
final long current = System.currentTimeMillis();
|
|
||||||
|
|
||||||
final int count = dataToUpdate.size();
|
|
||||||
for (int n = 0; n < count; n++) {
|
|
||||||
final SearchIndexableData data = dataToUpdate.get(n);
|
|
||||||
try {
|
|
||||||
indexOneSearchIndexableData(database, localeStr, data, nonIndexableKeys);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "Cannot index: " + (data != null ? data.className : data)
|
|
||||||
+ " for locale: " + localeStr, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final long now = System.currentTimeMillis();
|
|
||||||
Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +
|
|
||||||
(now - current) + " millis");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean processDataToDelete(SQLiteDatabase database, String localeStr,
|
|
||||||
List<SearchIndexableData> dataToDelete) {
|
|
||||||
|
|
||||||
boolean result = false;
|
|
||||||
final long current = System.currentTimeMillis();
|
|
||||||
|
|
||||||
final int count = dataToDelete.size();
|
|
||||||
for (int n = 0; n < count; n++) {
|
|
||||||
final SearchIndexableData data = dataToDelete.get(n);
|
|
||||||
if (data == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!TextUtils.isEmpty(data.className)) {
|
|
||||||
delete(database, IndexDatabaseHelper.IndexColumns.CLASS_NAME, data.className);
|
|
||||||
} else {
|
|
||||||
if (data instanceof SearchIndexableRaw) {
|
|
||||||
final SearchIndexableRaw raw = (SearchIndexableRaw) data;
|
|
||||||
if (!TextUtils.isEmpty(raw.title)) {
|
|
||||||
delete(database, IndexDatabaseHelper.IndexColumns.DATA_TITLE,
|
|
||||||
raw.title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final long now = System.currentTimeMillis();
|
|
||||||
Log.d(LOG_TAG, "Deleting data for locale '" + localeStr + "' took " +
|
|
||||||
(now - current) + " millis");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int delete(SQLiteDatabase database, String columName, String value) {
|
|
||||||
final String whereClause = columName + "=?";
|
|
||||||
final String[] whereArgs = new String[]{value};
|
|
||||||
|
|
||||||
return database.delete(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, whereClause,
|
|
||||||
whereArgs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,37 +17,65 @@
|
|||||||
|
|
||||||
package com.android.settings.search2;
|
package com.android.settings.search2;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ProviderInfo;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.net.Uri;
|
||||||
import android.provider.SearchIndexableResource;
|
import android.provider.SearchIndexableResource;
|
||||||
|
import android.provider.SearchIndexablesContract;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SettingsRobolectricTestRunner;
|
import com.android.settings.SettingsRobolectricTestRunner;
|
||||||
import com.android.settings.TestConfig;
|
import com.android.settings.TestConfig;
|
||||||
import com.android.settings.search.IndexDatabaseHelper;
|
import com.android.settings.search.IndexDatabaseHelper;
|
||||||
import com.android.settings.search.SearchIndexableRaw;
|
import com.android.settings.search.SearchIndexableRaw;
|
||||||
import com.android.settings.testutils.DatabaseTestUtils;
|
import com.android.settings.testutils.DatabaseTestUtils;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowDatabaseIndexingUtils;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowRunnableAsyncTask;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
import org.robolectric.shadows.ShadowApplication;
|
import org.robolectric.shadows.ShadowApplication;
|
||||||
|
import org.robolectric.shadows.ShadowContentResolver;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyInt;
|
||||||
|
import static org.mockito.Matchers.anyList;
|
||||||
|
import static org.mockito.Matchers.anyMap;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
|
||||||
|
shadows={ShadowRunnableAsyncTask.class})
|
||||||
public class DatabaseIndexingManagerTest {
|
public class DatabaseIndexingManagerTest {
|
||||||
private final String localeStr = "en_US";
|
private final String localeStr = "en_US";
|
||||||
|
|
||||||
@@ -75,15 +103,30 @@ public class DatabaseIndexingManagerTest {
|
|||||||
private final int userId = -1;
|
private final int userId = -1;
|
||||||
private final boolean enabled = true;
|
private final boolean enabled = true;
|
||||||
|
|
||||||
|
private final String AUTHORITY_ONE = "authority";
|
||||||
|
private final String PACKAGE_ONE = "com.android.settings";
|
||||||
|
|
||||||
|
private final String TITLE_ONE = "title one";
|
||||||
|
private final String TITLE_TWO = "title two";
|
||||||
|
private final String KEY_ONE = "key one";
|
||||||
|
private final String KEY_TWO = "key two";
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
|
||||||
private DatabaseIndexingManager mManager;
|
private DatabaseIndexingManager mManager;
|
||||||
private SQLiteDatabase mDb;
|
private SQLiteDatabase mDb;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PackageManager mPackageManager;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
mContext = ShadowApplication.getInstance().getApplicationContext();
|
MockitoAnnotations.initMocks(this);
|
||||||
mManager = spy(new DatabaseIndexingManager(mContext, mContext.getPackageName()));
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
|
mManager = spy(new DatabaseIndexingManager(mContext,"com.android.settings"));
|
||||||
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
|
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
|
||||||
|
|
||||||
|
doReturn(mPackageManager).when(mContext).getPackageManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -126,7 +169,7 @@ public class DatabaseIndexingManagerTest {
|
|||||||
// Tests for the flow: IndexOneRaw -> UpdateOneRowWithFilteredData -> UpdateOneRow
|
// Tests for the flow: IndexOneRaw -> UpdateOneRowWithFilteredData -> UpdateOneRow
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsertRawColumn_RowInserted() {
|
public void testInsertRawColumn_rowInserted() {
|
||||||
SearchIndexableRaw raw = getFakeRaw();
|
SearchIndexableRaw raw = getFakeRaw();
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
|
mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
@@ -134,7 +177,7 @@ public class DatabaseIndexingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsertRawColumn_RowMatches() {
|
public void testInsertRawColumn_rowMatches() {
|
||||||
SearchIndexableRaw raw = getFakeRaw();
|
SearchIndexableRaw raw = getFakeRaw();
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
|
mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
@@ -185,7 +228,7 @@ public class DatabaseIndexingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsertRawColumnMismatchedLocale_NoRowInserted() {
|
public void testInsertRawColumn_mismatchedLocale_noRowInserted() {
|
||||||
SearchIndexableRaw raw = getFakeRaw("ca-fr");
|
SearchIndexableRaw raw = getFakeRaw("ca-fr");
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
|
mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
@@ -206,18 +249,18 @@ public class DatabaseIndexingManagerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testAddResource_RowsInserted() {
|
public void testAddResource_RowsInserted() {
|
||||||
SearchIndexableResource resource = getFakeResource(R.xml.ia_display_settings);
|
SearchIndexableResource resource = getFakeResource(R.xml.ia_display_settings);
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
|
||||||
new HashMap<>());
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
assertThat(cursor.getCount()).isEqualTo(16);
|
assertThat(cursor.getCount()).isEqualTo(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddResourceWithNIKs_RowsInsertedDisabled() {
|
public void testAddResource_withNIKs_rowsInsertedDisabled() {
|
||||||
SearchIndexableResource resource = getFakeResource(R.xml.ia_display_settings);
|
SearchIndexableResource resource = getFakeResource(R.xml.ia_display_settings);
|
||||||
// Only add 2 of 16 items to be disabled.
|
// Only add 2 of 16 items to be disabled.
|
||||||
String[] keys = {"brightness", "wallpaper"};
|
String[] keys = {"brightness", "wallpaper"};
|
||||||
Map<String, List<String>> niks = getNonIndexableKeys(keys);
|
Map<String, Set<String>> niks = getNonIndexableKeys(keys);
|
||||||
|
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, niks);
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, niks);
|
||||||
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
|
||||||
@@ -227,10 +270,9 @@ public class DatabaseIndexingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddResourceHeader_RowsMatch() {
|
public void testAddResourceHeader_rowsMatch() {
|
||||||
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
|
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
|
||||||
new HashMap<>());
|
|
||||||
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
|
||||||
cursor.moveToPosition(1);
|
cursor.moveToPosition(1);
|
||||||
@@ -280,7 +322,7 @@ public class DatabaseIndexingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddResourceWithChildFragment_shouldUpdateSiteMapDb() {
|
public void testAddResource_withChildFragment_shouldUpdateSiteMapDb() {
|
||||||
// FIXME: This test was failing. (count = 6 at the end)
|
// FIXME: This test was failing. (count = 6 at the end)
|
||||||
|
|
||||||
// SearchIndexableResource resource = getFakeResource(R.xml.network_and_internet);
|
// SearchIndexableResource resource = getFakeResource(R.xml.network_and_internet);
|
||||||
@@ -305,10 +347,9 @@ public class DatabaseIndexingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddResourceCustomSetting_RowsMatch() {
|
public void testAddResource_customSetting_rowsMatch() {
|
||||||
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
|
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
|
||||||
new HashMap<>());
|
|
||||||
final String prefTitle =
|
final String prefTitle =
|
||||||
mContext.getString(R.string.fingerprint_swipe_for_notifications_title);
|
mContext.getString(R.string.fingerprint_swipe_for_notifications_title);
|
||||||
final String prefSummary =
|
final String prefSummary =
|
||||||
@@ -363,10 +404,9 @@ public class DatabaseIndexingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddResourceCheckboxPreference_RowsMatch() {
|
public void testAddResource_checkboxPreference_rowsMatch() {
|
||||||
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
|
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
|
||||||
new HashMap<>());
|
|
||||||
|
|
||||||
/* Should return 6 results, with the following titles:
|
/* Should return 6 results, with the following titles:
|
||||||
* Advanced Settings, Apps, Manage Apps, Preferred install location, Running Services
|
* Advanced Settings, Apps, Manage Apps, Preferred install location, Running Services
|
||||||
@@ -418,10 +458,9 @@ public class DatabaseIndexingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddResourceListPreference_RowsMatch() {
|
public void testAddResource_listPreference_rowsMatch() {
|
||||||
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
|
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
|
||||||
new HashMap<>());
|
|
||||||
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
|
||||||
cursor.moveToPosition(3);
|
cursor.moveToPosition(3);
|
||||||
@@ -476,25 +515,23 @@ public class DatabaseIndexingManagerTest {
|
|||||||
// UpdateOneRowWithFilteredData -> UpdateOneRow
|
// UpdateOneRowWithFilteredData -> UpdateOneRow
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResourceProvider_RowInserted() {
|
public void testResourceProvider_rowInserted() {
|
||||||
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
|
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
|
||||||
resource.xmlResId = 0;
|
resource.xmlResId = 0;
|
||||||
resource.className = "com.android.settings.display.ScreenZoomSettings";
|
resource.className = "com.android.settings.display.ScreenZoomSettings";
|
||||||
|
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
|
||||||
new HashMap<>());
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
assertThat(cursor.getCount()).isEqualTo(1);
|
assertThat(cursor.getCount()).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResourceProvider_Matches() {
|
public void testResourceProvider_rowMatches() {
|
||||||
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
|
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
|
||||||
resource.xmlResId = 0;
|
resource.xmlResId = 0;
|
||||||
resource.className = "com.android.settings.display.ScreenZoomSettings";
|
resource.className = "com.android.settings.display.ScreenZoomSettings";
|
||||||
|
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
|
||||||
new HashMap<>());
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
cursor.moveToPosition(0);
|
cursor.moveToPosition(0);
|
||||||
|
|
||||||
@@ -544,23 +581,21 @@ public class DatabaseIndexingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResourceProvider_ResourceRowInserted() {
|
public void testResourceProvider_resourceRowInserted() {
|
||||||
SearchIndexableResource resource = getFakeResource(0);
|
SearchIndexableResource resource = getFakeResource(0);
|
||||||
resource.className = "com.android.settings.LegalSettings";
|
resource.className = "com.android.settings.LegalSettings";
|
||||||
|
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
|
||||||
new HashMap<>());
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
assertThat(cursor.getCount()).isEqualTo(6);
|
assertThat(cursor.getCount()).isEqualTo(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResourceProvider_ResourceRowMatches() {
|
public void testResourceProvider_resourceRowMatches() {
|
||||||
SearchIndexableResource resource = getFakeResource(0);
|
SearchIndexableResource resource = getFakeResource(0);
|
||||||
resource.className = "com.android.settings.display.ScreenZoomSettings";
|
resource.className = "com.android.settings.display.ScreenZoomSettings";
|
||||||
|
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
|
||||||
new HashMap<>());
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
|
||||||
cursor.moveToPosition(0);
|
cursor.moveToPosition(0);
|
||||||
|
|
||||||
@@ -611,12 +646,12 @@ public class DatabaseIndexingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResourceProvider_DisabledResourceRowsInserted() {
|
public void testResourceProvider_disabledResource_rowsInserted() {
|
||||||
SearchIndexableResource resource = getFakeResource(0);
|
SearchIndexableResource resource = getFakeResource(0);
|
||||||
resource.className = "com.android.settings.LegalSettings";
|
resource.className = "com.android.settings.LegalSettings";
|
||||||
|
|
||||||
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
|
||||||
new HashMap<String, List<String>>());
|
new HashMap<String, Set<String>>());
|
||||||
|
|
||||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
|
||||||
assertThat(cursor.getCount()).isEqualTo(2);
|
assertThat(cursor.getCount()).isEqualTo(2);
|
||||||
@@ -625,7 +660,7 @@ public class DatabaseIndexingManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResourceWithTitleAndSettingName_TitleNotInserted() {
|
public void testResource_withTitleAndSettingName_titleNotInserted() {
|
||||||
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
|
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
|
||||||
mManager.indexFromResource(mDb, localeStr, resource, new ArrayList<String>());
|
mManager.indexFromResource(mDb, localeStr, resource, new ArrayList<String>());
|
||||||
|
|
||||||
@@ -634,6 +669,176 @@ public class DatabaseIndexingManagerTest {
|
|||||||
assertThat(cursor.getCount()).isEqualTo(1);
|
assertThat(cursor.getCount()).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test new public indexing flow
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Config(shadows= {
|
||||||
|
ShadowDatabaseIndexingUtils.class,
|
||||||
|
})
|
||||||
|
public void testPerformIndexing_fullIndex_getsDataFromProviders() {
|
||||||
|
DummyProvider provider = new DummyProvider();
|
||||||
|
provider.onCreate();
|
||||||
|
ShadowContentResolver.registerProvider(
|
||||||
|
AUTHORITY_ONE, provider
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test that Indexables are added for Full indexing
|
||||||
|
when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
|
||||||
|
.thenReturn(getDummyResolveInfo());
|
||||||
|
|
||||||
|
DatabaseIndexingManager manager =
|
||||||
|
spy(new DatabaseIndexingManager(mContext, "com.android.settings"));
|
||||||
|
doReturn(false).when(manager).isLocaleIndexed();
|
||||||
|
|
||||||
|
manager.performIndexing();
|
||||||
|
|
||||||
|
verify(manager).updateDatabase(false, Locale.getDefault().toString());
|
||||||
|
|
||||||
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
|
cursor.moveToPosition(0);
|
||||||
|
|
||||||
|
// Data Title
|
||||||
|
assertThat(cursor.getString(2)).isEqualTo(TITLE_ONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Config(shadows= {
|
||||||
|
ShadowDatabaseIndexingUtils.class,
|
||||||
|
})
|
||||||
|
public void testPerformIndexing_incrementalIndex_noDataAdded() {
|
||||||
|
DummyProvider provider = new DummyProvider();
|
||||||
|
provider.onCreate();
|
||||||
|
ShadowContentResolver.registerProvider(
|
||||||
|
AUTHORITY_ONE, provider
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test that Indexables are added for Full indexing
|
||||||
|
when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
|
||||||
|
.thenReturn(getDummyResolveInfo());
|
||||||
|
|
||||||
|
DatabaseIndexingManager manager =
|
||||||
|
spy(new DatabaseIndexingManager(mContext, "com.android.settings"));
|
||||||
|
doReturn(true).when(manager).isLocaleIndexed();
|
||||||
|
|
||||||
|
manager.performIndexing();
|
||||||
|
|
||||||
|
final Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||||
|
|
||||||
|
assertThat(cursor.getCount()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFullUpdatedDatabase_noData_addDataToDatabaseNotCalled() {
|
||||||
|
mManager.updateDatabase(false, localeStr);
|
||||||
|
mManager.mDataToProcess.dataToUpdate.clear();
|
||||||
|
verify(mManager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(),
|
||||||
|
anyList(), anyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFullUpdatedDatabase_updatedDataInDatabaseNotCalled() {
|
||||||
|
mManager.updateDatabase(false, localeStr);
|
||||||
|
verify(mManager, times(0)).updateDataInDatabase(any(SQLiteDatabase.class), anyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLocaleUpdated_afterIndexing_localeAdded() {
|
||||||
|
mManager.updateDatabase(false, localeStr);
|
||||||
|
assertThat(IndexDatabaseHelper.getInstance(mContext)
|
||||||
|
.isLocaleAlreadyIndexed(mContext, localeStr)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateDatabase_newEligibleData_addedToDatabase() {
|
||||||
|
// Test that addDataToDatabase is called when dataToUpdate is non-empty
|
||||||
|
mManager.mDataToProcess.dataToUpdate.add(getFakeRaw());
|
||||||
|
mManager.updateDatabase(false, localeStr);
|
||||||
|
|
||||||
|
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(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(entries);
|
||||||
|
// Keywords
|
||||||
|
assertThat(cursor.getString(9)).isEqualTo(spaceDelimittedKeywords);
|
||||||
|
// Screen Title
|
||||||
|
assertThat(cursor.getString(10)).isEqualTo(screenTitle);
|
||||||
|
// 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)).isNotNull();
|
||||||
|
// User Id
|
||||||
|
assertThat(cursor.getInt(18)).isEqualTo(userId);
|
||||||
|
// Payload Type - default is 0
|
||||||
|
assertThat(cursor.getInt(19)).isEqualTo(0);
|
||||||
|
// Payload
|
||||||
|
assertThat(cursor.getBlob(20)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateDataInDatabase_enabledResultsAreNonIndexable_becomeDisabled() {
|
||||||
|
// Both results are enabled, and then TITLE_ONE gets disabled.
|
||||||
|
final boolean enabled = true;
|
||||||
|
insertSpecialCase(TITLE_ONE, enabled, KEY_ONE);
|
||||||
|
insertSpecialCase(TITLE_TWO, enabled, KEY_TWO);
|
||||||
|
Map<String, Set<String>> niks = new ArrayMap<>();
|
||||||
|
Set<String> keys = new HashSet<>();
|
||||||
|
keys.add(KEY_ONE);
|
||||||
|
niks.put(targetPackage, keys);
|
||||||
|
|
||||||
|
mManager.updateDataInDatabase(mDb, niks);
|
||||||
|
|
||||||
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
|
||||||
|
cursor.moveToPosition(0);
|
||||||
|
|
||||||
|
assertThat(cursor.getString(2)).isEqualTo(TITLE_ONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateDataInDatabase_DisabledResultsAreIndexable_BecomeEnabled() {
|
||||||
|
// Both results are initially disabled, and then TITLE_TWO gets enabled.
|
||||||
|
final boolean enabled = false;
|
||||||
|
insertSpecialCase(TITLE_ONE, enabled, KEY_ONE);
|
||||||
|
insertSpecialCase(TITLE_TWO, enabled, KEY_TWO);
|
||||||
|
Map<String, Set<String>> niks = new ArrayMap<>();
|
||||||
|
Set<String> keys = new HashSet<>();
|
||||||
|
keys.add(KEY_ONE);
|
||||||
|
niks.put(targetPackage, keys);
|
||||||
|
|
||||||
|
mManager.updateDataInDatabase(mDb, niks);
|
||||||
|
|
||||||
|
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
|
||||||
|
cursor.moveToPosition(0);
|
||||||
|
|
||||||
|
assertThat(cursor.getString(2)).isEqualTo(TITLE_TWO);
|
||||||
|
}
|
||||||
|
|
||||||
// Util functions
|
// Util functions
|
||||||
|
|
||||||
private SearchIndexableRaw getFakeRaw() {
|
private SearchIndexableRaw getFakeRaw() {
|
||||||
@@ -676,10 +881,119 @@ public class DatabaseIndexingManagerTest {
|
|||||||
return sir;
|
return sir;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, List<String>> getNonIndexableKeys(String[] keys) {
|
private Map<String, Set<String>> getNonIndexableKeys(String[] keys) {
|
||||||
Map<String, List<String>> niks = new HashMap<>();
|
Map<String, Set<String>> niks = new HashMap<>();
|
||||||
List<String> keysList = new ArrayList<>(Arrays.asList(keys));
|
Set<String> keysList = new HashSet<>();
|
||||||
|
keysList.addAll(Arrays.asList(keys));
|
||||||
niks.put(packageName, keysList);
|
niks.put(packageName, keysList);
|
||||||
return niks;
|
return niks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<ResolveInfo> getDummyResolveInfo() {
|
||||||
|
List<ResolveInfo> infoList = new ArrayList<>();
|
||||||
|
ResolveInfo info = new ResolveInfo();
|
||||||
|
info.providerInfo = new ProviderInfo();
|
||||||
|
info.providerInfo.exported = true;
|
||||||
|
info.providerInfo.authority = AUTHORITY_ONE;
|
||||||
|
info.providerInfo.packageName = PACKAGE_ONE;
|
||||||
|
infoList.add(info);
|
||||||
|
|
||||||
|
return infoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO move this method and its counterpart in CursorToSearchResultConverterTest into
|
||||||
|
// a util class with public fields to assert values.
|
||||||
|
private Cursor getDummyCursor() {
|
||||||
|
MatrixCursor cursor = new MatrixCursor(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS);
|
||||||
|
final String BLANK = "";
|
||||||
|
|
||||||
|
ArrayList<String> item =
|
||||||
|
new ArrayList<>(SearchIndexablesContract.INDEXABLES_RAW_COLUMNS.length);
|
||||||
|
item.add("42"); // Rank
|
||||||
|
item.add(TITLE_ONE); // Title
|
||||||
|
item.add(BLANK); // Summary on
|
||||||
|
item.add(BLANK); // summary off
|
||||||
|
item.add(BLANK); // entries
|
||||||
|
item.add(BLANK); // keywords
|
||||||
|
item.add(BLANK); // screen title
|
||||||
|
item.add(BLANK); // classname
|
||||||
|
item.add("123"); // Icon
|
||||||
|
item.add(BLANK); // Intent action
|
||||||
|
item.add(BLANK); // target package
|
||||||
|
item.add(BLANK); // target class
|
||||||
|
item.add(KEY_ONE); // Key
|
||||||
|
item.add("-1"); // userId
|
||||||
|
cursor.addRow(item);
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertSpecialCase(String specialCase, boolean enabled, String key) {
|
||||||
|
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DOCID, specialCase.hashCode());
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, localeStr);
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, specialCase);
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON_NORMALIZED, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DATA_ENTRIES, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.CLASS_NAME, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.SCREEN_TITLE, "Moves");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.INTENT_ACTION, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, targetPackage);
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.ENABLED, enabled);
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, key);
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
|
||||||
|
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, (String) null);
|
||||||
|
|
||||||
|
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DummyProvider extends ContentProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
|
||||||
|
@Nullable String selection, @Nullable String[] selectionArgs,
|
||||||
|
@Nullable String sortOrder) {
|
||||||
|
if (uri.toString().contains("xml")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getDummyCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType(@NonNull Uri uri) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(@NonNull Uri uri, @Nullable String selection,
|
||||||
|
@Nullable String[] selectionArgs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(@NonNull Uri uri, @Nullable ContentValues values,
|
||||||
|
@Nullable String selection, @Nullable String[] selectionArgs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -29,5 +29,4 @@ public class ShadowContentResolver {
|
|||||||
public static SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
|
public static SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
|
||||||
return new SyncAdapterType[0];
|
return new SyncAdapterType[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.testutils.shadow;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import com.android.settings.search2.DatabaseIndexingUtils;
|
||||||
|
import org.robolectric.annotation.Implementation;
|
||||||
|
import org.robolectric.annotation.Implements;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shadow of {@link DatabaseIndexingUtils}
|
||||||
|
*/
|
||||||
|
@Implements(DatabaseIndexingUtils.class)
|
||||||
|
public class ShadowDatabaseIndexingUtils {
|
||||||
|
@Implementation
|
||||||
|
public static boolean isWellKnownProvider(ResolveInfo info, Context context) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.testutils.shadow;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import org.robolectric.annotation.Implementation;
|
||||||
|
import org.robolectric.annotation.Implements;
|
||||||
|
import org.robolectric.shadows.ShadowAsyncTask;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shadow async task to handle runnables in roboletric
|
||||||
|
*/
|
||||||
|
@Implements(AsyncTask.class)
|
||||||
|
public class ShadowRunnableAsyncTask<Params, Progress, Result> extends
|
||||||
|
ShadowAsyncTask<Params, Progress, Result> {
|
||||||
|
|
||||||
|
@Implementation
|
||||||
|
public AsyncTask<Params, Progress, Result> executeOnExecutor(Executor executor,
|
||||||
|
Params... params) {
|
||||||
|
return super.execute(params);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user