Merge "SearchFragment removes stale data from the database"

This commit is contained in:
TreeHugger Robot
2017-03-16 00:17:11 +00:00
committed by Android (Google) Code Review
5 changed files with 717 additions and 287 deletions

View File

@@ -27,7 +27,6 @@ import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteFullException;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.SearchIndexableData;
@@ -53,10 +52,12 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
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_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.
* 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 {
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_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;
/**
* 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 DatabaseIndexingManager.UpdateData mDataToProcess =
new DatabaseIndexingManager.UpdateData();
@VisibleForTesting
final UpdateData mDataToProcess = new UpdateData();
private Context mContext;
public DatabaseIndexingManager(Context context, String baseAuthority) {
@@ -157,31 +152,201 @@ public class DatabaseIndexingManager {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
List<ResolveInfo> list =
mContext.getPackageManager().queryIntentContentProviders(intent, 0);
final int size = list.size();
for (int n = 0; n < size; n++) {
final ResolveInfo info = list.get(n);
if (!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();
performIndexing();
}
});
}
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 {
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 Context context = mBaseAuthority.equals(authority) ?
@@ -202,11 +367,12 @@ public class DatabaseIndexingManager {
}
}
private void addNonIndexablesKeysFromRemoteProvider(String packageName,
@VisibleForTesting
void addNonIndexablesKeysFromRemoteProvider(String packageName,
String authority) {
final List<String> keys =
getNonIndexablesKeysFromRemoteProvider(packageName, authority);
addNonIndexableKeys(packageName, keys);
addNonIndexableKeys(packageName, new HashSet<>(keys));
}
private List<String> getNonIndexablesKeysFromRemoteProvider(String packageName,
@@ -235,7 +401,7 @@ public class DatabaseIndexingManager {
return EMPTY_LIST;
}
List<String> result = new ArrayList<String>();
final List<String> result = new ArrayList<>();
try {
final int count = cursor.getCount();
if (count > 0) {
@@ -256,31 +422,19 @@ public class DatabaseIndexingManager {
}
}
public void deleteIndexableData(SearchIndexableData data) {
synchronized (mDataToProcess) {
mDataToProcess.dataToDelete.add(data);
}
}
public void addNonIndexableKeys(String authority, List<String> keys) {
public void addNonIndexableKeys(String authority, Set<String> keys) {
synchronized (mDataToProcess) {
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
*
* @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 className the class name (typically a fragment name).
* @param rebuild true means that you want to delete the data from the Index first.
* @param includeInSearchResults true means that you want the bit "enabled" set so that the
* data will be seen included into the search results
* data will be seen included into the search results
*/
public void updateFromClassNameResource(String className, final boolean rebuild,
boolean includeInSearchResults) {
@@ -297,12 +451,8 @@ public class DatabaseIndexingManager {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
if (rebuild) {
deleteIndexableData(res);
}
addIndexableData(res);
mDataToProcess.forceUpdate = true;
updateInternal();
performIndexing();
res.enabled = false;
}
});
@@ -313,8 +463,7 @@ public class DatabaseIndexingManager {
@Override
public void run() {
addIndexableData(data);
mDataToProcess.forceUpdate = true;
updateInternal();
performIndexing();
}
});
}
@@ -347,16 +496,6 @@ public class DatabaseIndexingManager {
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,
Uri uri, String[] projection, int baseRank) {
@@ -468,7 +607,7 @@ public class DatabaseIndexingManager {
}
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) {
indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);
} else if (data instanceof SearchIndexableRaw) {
@@ -502,7 +641,7 @@ public class DatabaseIndexingManager {
}
private void indexOneResource(SQLiteDatabase database, String localeStr,
SearchIndexableResource sir, Map<String, List<String>> nonIndexableKeysFromResource) {
SearchIndexableResource sir, Map<String, Set<String>> nonIndexableKeysFromResource) {
if (sir == null) {
Log.e(LOG_TAG, "Cannot index a null resource!");
@@ -512,9 +651,9 @@ public class DatabaseIndexingManager {
final List<String> nonIndexableKeys = new ArrayList<String>();
if (sir.xmlResId > SearchIndexableResources.NO_DATA_RES_ID) {
List<String> resNonIndxableKeys = nonIndexableKeysFromResource.get(sir.packageName);
if (resNonIndxableKeys != null && resNonIndxableKeys.size() > 0) {
nonIndexableKeys.addAll(resNonIndxableKeys);
Set<String> resNonIndexableKeys = nonIndexableKeysFromResource.get(sir.packageName);
if (resNonIndexableKeys != null && resNonIndexableKeys.size() > 0) {
nonIndexableKeys.addAll(resNonIndexableKeys);
}
indexFromResource(database, localeStr, sir, nonIndexableKeys);
@@ -605,6 +744,7 @@ public class DatabaseIndexingManager {
headerKeywords = XmlParserUtils.getDataKeywords(context, attrs);
enabled = !nonIndexableKeys.contains(key);
// TODO: Set payload type for header results
DatabaseRow.Builder headerBuilder = new DatabaseRow.Builder();
headerBuilder.setLocale(localeStr)
.setEntries(null)
@@ -799,31 +939,29 @@ public class DatabaseIndexingManager {
ContentValues values = new ContentValues();
values.put(IndexDatabaseHelper.IndexColumns.DOCID, row.getDocId());
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,
row.normalizedSummaryOn);
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF, row.updatedSummaryOff);
values.put(IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_OFF_NORMALIZED,
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);
values.put(LOCALE, row.locale);
values.put(DATA_RANK, row.rank);
values.put(DATA_TITLE, row.updatedTitle);
values.put(DATA_TITLE_NORMALIZED, row.normalizedTitle);
values.put(DATA_SUMMARY_ON, row.updatedSummaryOn);
values.put(DATA_SUMMARY_ON_NORMALIZED, row.normalizedSummaryOn);
values.put(DATA_SUMMARY_OFF, row.updatedSummaryOff);
values.put(DATA_SUMMARY_OFF_NORMALIZED, row.normalizedSummaryOff);
values.put(DATA_ENTRIES, row.entries);
values.put(DATA_KEYWORDS, row.spaceDelimitedKeywords);
values.put(CLASS_NAME, row.className);
values.put(SCREEN_TITLE, row.screenTitle);
values.put(INTENT_ACTION, row.intentAction);
values.put(INTENT_TARGET_PACKAGE, row.intentTargetPackage);
values.put(INTENT_TARGET_CLASS, row.intentTargetClass);
values.put(ICON, row.iconResId);
values.put(ENABLED, row.enabled);
values.put(DATA_KEY_REF, row.key);
values.put(USER_ID, row.userId);
values.put(PAYLOAD_TYPE, row.payloadType);
values.put(PAYLOAD, row.payload);
database.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
database.replaceOrThrow(TABLE_PREFS_INDEX, null, values);
if (!TextUtils.isEmpty(row.className) && !TextUtils.isEmpty(row.childClassName)) {
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,
Void> {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static class UpdateData {
public List<SearchIndexableData> dataToUpdate;
public List<SearchIndexableData> dataToDisable;
public Map<String, Set<String>> nonIndexableKeys;
@Override
protected void onPreExecute() {
super.onPreExecute();
mIsAvailable.set(false);
public UpdateData() {
dataToUpdate = new ArrayList<>();
dataToDisable = new ArrayList<>();
nonIndexableKeys = new HashMap<>();
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
mIsAvailable.set(true);
public UpdateData(UpdateData other) {
dataToUpdate = new ArrayList<>(other.dataToUpdate);
dataToDisable = new ArrayList<>(other.dataToDisable);
nonIndexableKeys = new HashMap<>(other.nonIndexableKeys);
}
@Override
protected Void doInBackground(DatabaseIndexingManager.UpdateData... params) {
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;
public UpdateData copy() {
return new UpdateData(this);
}
private boolean processDataToUpdate(SQLiteDatabase database, String localeStr,
List<SearchIndexableData> dataToUpdate, Map<String, List<String>> nonIndexableKeys,
boolean forceUpdate) {
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);
public void clear() {
dataToUpdate.clear();
dataToDisable.clear();
nonIndexableKeys.clear();
}
}

View File

@@ -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");
* you may not use this file except in compliance with the License.
@@ -17,37 +17,65 @@
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.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.provider.SearchIndexableResource;
import android.provider.SearchIndexablesContract;
import android.util.ArrayMap;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search.IndexDatabaseHelper;
import com.android.settings.search.SearchIndexableRaw;
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.Before;
import org.junit.Test;
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.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowContentResolver;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
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.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@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 {
private final String localeStr = "en_US";
@@ -75,15 +103,30 @@ public class DatabaseIndexingManagerTest {
private final int userId = -1;
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 DatabaseIndexingManager mManager;
private SQLiteDatabase mDb;
@Mock
private PackageManager mPackageManager;
@Before
public void setUp() {
mContext = ShadowApplication.getInstance().getApplicationContext();
mManager = spy(new DatabaseIndexingManager(mContext, mContext.getPackageName()));
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mManager = spy(new DatabaseIndexingManager(mContext,"com.android.settings"));
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
doReturn(mPackageManager).when(mContext).getPackageManager();
}
@After
@@ -126,7 +169,7 @@ public class DatabaseIndexingManagerTest {
// Tests for the flow: IndexOneRaw -> UpdateOneRowWithFilteredData -> UpdateOneRow
@Test
public void testInsertRawColumn_RowInserted() {
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);
@@ -134,7 +177,7 @@ public class DatabaseIndexingManagerTest {
}
@Test
public void testInsertRawColumn_RowMatches() {
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);
@@ -185,7 +228,7 @@ public class DatabaseIndexingManagerTest {
}
@Test
public void testInsertRawColumnMismatchedLocale_NoRowInserted() {
public void testInsertRawColumn_mismatchedLocale_noRowInserted() {
SearchIndexableRaw raw = getFakeRaw("ca-fr");
mManager.indexOneSearchIndexableData(mDb, localeStr, raw, null /* Non-indexable keys */);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
@@ -206,18 +249,18 @@ public class DatabaseIndexingManagerTest {
@Test
public void testAddResource_RowsInserted() {
SearchIndexableResource resource = getFakeResource(R.xml.ia_display_settings);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
new HashMap<>());
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(16);
}
@Test
public void testAddResourceWithNIKs_RowsInsertedDisabled() {
public void testAddResource_withNIKs_rowsInsertedDisabled() {
SearchIndexableResource resource = getFakeResource(R.xml.ia_display_settings);
// Only add 2 of 16 items to be disabled.
String[] keys = {"brightness", "wallpaper"};
Map<String, List<String>> niks = getNonIndexableKeys(keys);
Map<String, Set<String>> niks = getNonIndexableKeys(keys);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, niks);
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
@@ -227,10 +270,9 @@ public class DatabaseIndexingManagerTest {
}
@Test
public void testAddResourceHeader_RowsMatch() {
public void testAddResourceHeader_rowsMatch() {
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
new HashMap<>());
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(1);
@@ -280,7 +322,7 @@ public class DatabaseIndexingManagerTest {
}
@Test
public void testAddResourceWithChildFragment_shouldUpdateSiteMapDb() {
public void testAddResource_withChildFragment_shouldUpdateSiteMapDb() {
// FIXME: This test was failing. (count = 6 at the end)
// SearchIndexableResource resource = getFakeResource(R.xml.network_and_internet);
@@ -305,10 +347,9 @@ public class DatabaseIndexingManagerTest {
}
@Test
public void testAddResourceCustomSetting_RowsMatch() {
public void testAddResource_customSetting_rowsMatch() {
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
new HashMap<>());
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
final String prefTitle =
mContext.getString(R.string.fingerprint_swipe_for_notifications_title);
final String prefSummary =
@@ -363,10 +404,9 @@ public class DatabaseIndexingManagerTest {
}
@Test
public void testAddResourceCheckboxPreference_RowsMatch() {
public void testAddResource_checkboxPreference_rowsMatch() {
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
new HashMap<>());
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
@@ -418,10 +458,9 @@ public class DatabaseIndexingManagerTest {
}
@Test
public void testAddResourceListPreference_RowsMatch() {
public void testAddResource_listPreference_rowsMatch() {
SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
new HashMap<>());
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(3);
@@ -476,25 +515,23 @@ public class DatabaseIndexingManagerTest {
// UpdateOneRowWithFilteredData -> UpdateOneRow
@Test
public void testResourceProvider_RowInserted() {
public void testResourceProvider_rowInserted() {
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
resource.xmlResId = 0;
resource.className = "com.android.settings.display.ScreenZoomSettings";
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
new HashMap<>());
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() {
public void testResourceProvider_rowMatches() {
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
resource.xmlResId = 0;
resource.className = "com.android.settings.display.ScreenZoomSettings";
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
new HashMap<>());
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
cursor.moveToPosition(0);
@@ -544,23 +581,21 @@ public class DatabaseIndexingManagerTest {
}
@Test
public void testResourceProvider_ResourceRowInserted() {
public void testResourceProvider_resourceRowInserted() {
SearchIndexableResource resource = getFakeResource(0);
resource.className = "com.android.settings.LegalSettings";
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
new HashMap<>());
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
assertThat(cursor.getCount()).isEqualTo(6);
}
@Test
public void testResourceProvider_ResourceRowMatches() {
public void testResourceProvider_resourceRowMatches() {
SearchIndexableResource resource = getFakeResource(0);
resource.className = "com.android.settings.display.ScreenZoomSettings";
mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
new HashMap<>());
mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>());
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index ORDER BY data_title", null);
cursor.moveToPosition(0);
@@ -611,12 +646,12 @@ public class DatabaseIndexingManagerTest {
}
@Test
public void testResourceProvider_DisabledResourceRowsInserted() {
public void testResourceProvider_disabledResource_rowsInserted() {
SearchIndexableResource resource = getFakeResource(0);
resource.className = "com.android.settings.LegalSettings";
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);
assertThat(cursor.getCount()).isEqualTo(2);
@@ -625,7 +660,7 @@ public class DatabaseIndexingManagerTest {
}
@Test
public void testResourceWithTitleAndSettingName_TitleNotInserted() {
public void testResource_withTitleAndSettingName_titleNotInserted() {
SearchIndexableResource resource = getFakeResource(R.xml.swipe_to_notification_settings);
mManager.indexFromResource(mDb, localeStr, resource, new ArrayList<String>());
@@ -634,6 +669,176 @@ public class DatabaseIndexingManagerTest {
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
private SearchIndexableRaw getFakeRaw() {
@@ -676,10 +881,119 @@ public class DatabaseIndexingManagerTest {
return sir;
}
private Map<String, List<String>> getNonIndexableKeys(String[] keys) {
Map<String, List<String>> niks = new HashMap<>();
List<String> keysList = new ArrayList<>(Arrays.asList(keys));
private Map<String, Set<String>> getNonIndexableKeys(String[] keys) {
Map<String, Set<String>> niks = new HashMap<>();
Set<String> keysList = new HashSet<>();
keysList.addAll(Arrays.asList(keys));
niks.put(packageName, keysList);
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;
}
}
}

View File

@@ -29,5 +29,4 @@ public class ShadowContentResolver {
public static SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
return new SyncAdapterType[0];
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}