Merge "Move a11y indexing from DynamicContentMonitor to loader" into oc-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0bfe384b93
@@ -20,7 +20,6 @@ import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -52,7 +51,6 @@ import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
@@ -727,40 +725,6 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
|
||||
|
||||
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
|
||||
List<SearchIndexableRaw> indexables = new ArrayList<>();
|
||||
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
AccessibilityManager accessibilityManager =
|
||||
context.getSystemService(AccessibilityManager.class);
|
||||
|
||||
String screenTitle = context.getResources().getString(
|
||||
R.string.accessibility_settings);
|
||||
|
||||
// Indexing all services, regardless if enabled.
|
||||
List<AccessibilityServiceInfo> services = accessibilityManager
|
||||
.getInstalledAccessibilityServiceList();
|
||||
final int serviceCount = services.size();
|
||||
for (int i = 0; i < serviceCount; i++) {
|
||||
AccessibilityServiceInfo service = services.get(i);
|
||||
if (service == null || service.getResolveInfo() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
|
||||
ComponentName componentName = new ComponentName(serviceInfo.packageName,
|
||||
serviceInfo.name);
|
||||
|
||||
SearchIndexableRaw indexable = new SearchIndexableRaw(context);
|
||||
indexable.key = componentName.flattenToString();
|
||||
indexable.title = service.getResolveInfo().loadLabel(packageManager).toString();
|
||||
indexable.screenTitle = screenTitle;
|
||||
indexables.add(indexable);
|
||||
}
|
||||
|
||||
return indexables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
|
||||
|
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.search;
|
||||
|
||||
import static com.android.settings.search.InstalledAppResultLoader.getWordDifference;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.accessibility.AccessibilitySettings;
|
||||
import com.android.settings.dashboard.SiteMapManager;
|
||||
import com.android.settings.utils.AsyncLoader;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class AccessibilityServiceResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
|
||||
|
||||
private static final int NAME_NO_MATCH = -1;
|
||||
|
||||
private List<String> mBreadcrumb;
|
||||
private SiteMapManager mSiteMapManager;
|
||||
@VisibleForTesting
|
||||
final String mQuery;
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
private final PackageManager mPackageManager;
|
||||
|
||||
|
||||
public AccessibilityServiceResultLoader(Context context, String query,
|
||||
SiteMapManager mapManager) {
|
||||
super(context);
|
||||
mSiteMapManager = mapManager;
|
||||
mPackageManager = context.getPackageManager();
|
||||
mAccessibilityManager =
|
||||
(AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
mQuery = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends SearchResult> loadInBackground() {
|
||||
final Set<SearchResult> results = new HashSet<>();
|
||||
final Context context = getContext();
|
||||
final List<AccessibilityServiceInfo> services = mAccessibilityManager
|
||||
.getInstalledAccessibilityServiceList();
|
||||
final String screenTitle = context.getString(R.string.accessibility_settings);
|
||||
for (AccessibilityServiceInfo service : services) {
|
||||
if (service == null) {
|
||||
continue;
|
||||
}
|
||||
final ResolveInfo resolveInfo = service.getResolveInfo();
|
||||
if (service.getResolveInfo() == null) {
|
||||
continue;
|
||||
}
|
||||
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||
final CharSequence title = resolveInfo.loadLabel(mPackageManager);
|
||||
final int wordDiff = getWordDifference(title.toString(), mQuery);
|
||||
if (wordDiff == NAME_NO_MATCH) {
|
||||
continue;
|
||||
}
|
||||
final Drawable icon;
|
||||
if (resolveInfo.getIconResource() == 0) {
|
||||
icon = ContextCompat.getDrawable(context, R.mipmap.ic_accessibility_generic);
|
||||
} else {
|
||||
icon = resolveInfo.loadIcon(mPackageManager);
|
||||
}
|
||||
final String componentName = new ComponentName(serviceInfo.packageName,
|
||||
serviceInfo.name).flattenToString();
|
||||
final Intent intent = DatabaseIndexingUtils.buildSubsettingIntent(context,
|
||||
AccessibilitySettings.class.getName(), componentName, screenTitle);
|
||||
|
||||
results.add(new SearchResult.Builder()
|
||||
.setTitle(title)
|
||||
.addBreadcrumbs(getBreadCrumb())
|
||||
.setPayload(new ResultPayload(intent))
|
||||
.setRank(wordDiff)
|
||||
.setIcon(icon)
|
||||
.setStableId(Objects.hash(screenTitle, componentName))
|
||||
.build());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<String> getBreadCrumb() {
|
||||
if (mBreadcrumb == null || mBreadcrumb.isEmpty()) {
|
||||
final Context context = getContext();
|
||||
mBreadcrumb = mSiteMapManager.buildBreadCrumb(
|
||||
context, AccessibilitySettings.class.getName(),
|
||||
context.getString(R.string.accessibility_settings));
|
||||
}
|
||||
return mBreadcrumb;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDiscardResult(Set<? extends SearchResult> result) {
|
||||
|
||||
}
|
||||
}
|
@@ -17,7 +17,6 @@
|
||||
package com.android.settings.search;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.app.Activity;
|
||||
import android.app.LoaderManager;
|
||||
import android.content.ContentResolver;
|
||||
@@ -41,13 +40,11 @@ import android.provider.UserDictionary;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.inputmethod.InputMethod;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.settings.accessibility.AccessibilitySettings;
|
||||
import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
|
||||
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
|
||||
import com.android.settings.inputmethod.VirtualKeyboardFragment;
|
||||
@@ -89,7 +86,6 @@ public final class DynamicIndexableContentMonitor implements
|
||||
@VisibleForTesting
|
||||
static void resetForTesting() {
|
||||
InputDevicesMonitor.getInstance().resetForTesting();
|
||||
AccessibilityServicesMonitor.getInstance().resetForTesting();
|
||||
InputMethodServicesMonitor.getInstance().resetForTesting();
|
||||
}
|
||||
|
||||
@@ -144,7 +140,6 @@ public final class DynamicIndexableContentMonitor implements
|
||||
InputDevicesMonitor.getInstance().initialize(context, mIndexManager);
|
||||
|
||||
// Start tracking packages.
|
||||
AccessibilityServicesMonitor.getInstance().initialize(context, mIndexManager);
|
||||
InputMethodServicesMonitor.getInstance().initialize(context, mIndexManager);
|
||||
}
|
||||
|
||||
@@ -301,86 +296,17 @@ public final class DynamicIndexableContentMonitor implements
|
||||
|
||||
private void postPackageAvailable(final String packageName) {
|
||||
getRegisteredHandler().postDelayed(() -> {
|
||||
AccessibilityServicesMonitor.getInstance().onPackageAvailable(packageName);
|
||||
InputMethodServicesMonitor.getInstance().onPackageAvailable(packageName);
|
||||
}, DELAY_PROCESS_PACKAGE_CHANGE);
|
||||
}
|
||||
|
||||
private void postPackageUnavailable(final String packageName) {
|
||||
getRegisteredHandler().postDelayed(() -> {
|
||||
AccessibilityServicesMonitor.getInstance().onPackageUnavailable(packageName);
|
||||
InputMethodServicesMonitor.getInstance().onPackageUnavailable(packageName);
|
||||
}, DELAY_PROCESS_PACKAGE_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
// A singleton that holds list of available accessibility services and updates search index.
|
||||
private static class AccessibilityServicesMonitor {
|
||||
|
||||
// Null if not initialized.
|
||||
@Nullable private DatabaseIndexingManager mIndexManager;
|
||||
private PackageManager mPackageManager;
|
||||
private final List<String> mAccessibilityServices = new ArrayList<>();
|
||||
|
||||
private AccessibilityServicesMonitor() {}
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final AccessibilityServicesMonitor INSTANCE =
|
||||
new AccessibilityServicesMonitor();
|
||||
}
|
||||
|
||||
static AccessibilityServicesMonitor getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
synchronized void resetForTesting() {
|
||||
mIndexManager = null;
|
||||
}
|
||||
|
||||
synchronized void initialize(Context context, DatabaseIndexingManager index) {
|
||||
if (mIndexManager != null) return;
|
||||
mIndexManager = index;
|
||||
mPackageManager = context.getPackageManager();
|
||||
mAccessibilityServices.clear();
|
||||
buildIndex();
|
||||
|
||||
// Cache accessibility service packages to know when they go away.
|
||||
AccessibilityManager accessibilityManager = (AccessibilityManager) context
|
||||
.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
for (final AccessibilityServiceInfo accessibilityService
|
||||
: accessibilityManager.getInstalledAccessibilityServiceList()) {
|
||||
ResolveInfo resolveInfo = accessibilityService.getResolveInfo();
|
||||
if (resolveInfo != null && resolveInfo.serviceInfo != null) {
|
||||
mAccessibilityServices.add(resolveInfo.serviceInfo.packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buildIndex() {
|
||||
mIndexManager.updateFromClassNameResource(AccessibilitySettings.class.getName(),
|
||||
true /* includeInSearchResults */);
|
||||
}
|
||||
|
||||
synchronized void onPackageAvailable(String packageName) {
|
||||
if (mIndexManager == null) return;
|
||||
if (mAccessibilityServices.contains(packageName)) return;
|
||||
|
||||
final Intent intent = getAccessibilityServiceIntent(packageName);
|
||||
final List<ResolveInfo> services = mPackageManager
|
||||
.queryIntentServices(intent, 0 /* flags */);
|
||||
if (services == null || services.isEmpty()) return;
|
||||
mAccessibilityServices.add(packageName);
|
||||
buildIndex();
|
||||
}
|
||||
|
||||
synchronized void onPackageUnavailable(String packageName) {
|
||||
if (mIndexManager == null) return;
|
||||
if (!mAccessibilityServices.remove(packageName)) return;
|
||||
buildIndex();
|
||||
}
|
||||
}
|
||||
|
||||
// A singleton that holds list of available input methods and updates search index.
|
||||
// Also it monitors user dictionary changes and updates search index.
|
||||
private static class InputMethodServicesMonitor extends ContentObserver {
|
||||
|
@@ -163,8 +163,9 @@ public class InstalledAppResultLoader extends AsyncLoader<Set<? extends SearchRe
|
||||
* appName: Abcde, query: bc, Returns NAME_NO_MATCH
|
||||
* appName: Abcde, query: xyz, Returns NAME_NO_MATCH
|
||||
* appName: Abc de, query: de, Returns 4
|
||||
* TODO: Move this to a common util class.
|
||||
*/
|
||||
private int getWordDifference(String appName, String query) {
|
||||
static int getWordDifference(String appName, String query) {
|
||||
if (TextUtils.isEmpty(appName) || TextUtils.isEmpty(query)) {
|
||||
return NAME_NO_MATCH;
|
||||
}
|
||||
|
@@ -42,6 +42,12 @@ public interface SearchFeatureProvider {
|
||||
*/
|
||||
InstalledAppResultLoader getInstalledAppSearchLoader(Context context, String query);
|
||||
|
||||
/**
|
||||
* Returns a new loader to search accessibility services.
|
||||
*/
|
||||
AccessibilityServiceResultLoader getAccessibilityServiceResultLoader(Context context,
|
||||
String query);
|
||||
|
||||
/**
|
||||
* Returns a new loader to get all recently saved queries search terms.
|
||||
*/
|
||||
@@ -95,8 +101,8 @@ public interface SearchFeatureProvider {
|
||||
/**
|
||||
* Query search results based on the input query.
|
||||
*
|
||||
* @param context application context
|
||||
* @param query input user query
|
||||
* @param context application context
|
||||
* @param query input user query
|
||||
* @param searchResultsRankerCallback {@link SearchResultsRankerCallback}
|
||||
*/
|
||||
default void querySearchResults(Context context, String query,
|
||||
@@ -112,8 +118,8 @@ public interface SearchFeatureProvider {
|
||||
/**
|
||||
* Notify that a search result is clicked.
|
||||
*
|
||||
* @param context application context
|
||||
* @param query input user query
|
||||
* @param context application context
|
||||
* @param query input user query
|
||||
* @param searchResult clicked result
|
||||
*/
|
||||
default void searchResultClicked(Context context, String query, SearchResult searchResult) {
|
||||
|
@@ -54,6 +54,13 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
||||
cleanQuery(query), getSiteMapManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityServiceResultLoader getAccessibilityServiceResultLoader(Context context,
|
||||
String query) {
|
||||
return new AccessibilityServiceResultLoader(context, cleanQuery(query),
|
||||
getSiteMapManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedQueryLoader getSavedQueryLoader(Context context) {
|
||||
return new SavedQueryLoader(context);
|
||||
|
@@ -82,8 +82,10 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
static final int LOADER_ID_DATABASE = 1;
|
||||
@VisibleForTesting
|
||||
static final int LOADER_ID_INSTALLED_APPS = 2;
|
||||
@VisibleForTesting
|
||||
static final int LOADER_ID_ACCESSIBILITY_SERVICES = 3;
|
||||
|
||||
private static final int NUM_QUERY_LOADERS = 2;
|
||||
private static final int NUM_QUERY_LOADERS = 3;
|
||||
|
||||
@VisibleForTesting
|
||||
AtomicInteger mUnfinishedLoadersCount = new AtomicInteger(NUM_QUERY_LOADERS);
|
||||
@@ -281,6 +283,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
loaderManager.destroyLoader(LOADER_ID_DATABASE);
|
||||
loaderManager.destroyLoader(LOADER_ID_INSTALLED_APPS);
|
||||
loaderManager.destroyLoader(LOADER_ID_ACCESSIBILITY_SERVICES);
|
||||
mShowingSavedQuery = true;
|
||||
mSavedQueryController.loadSavedQueries();
|
||||
mSearchFeatureProvider.hideFeedbackButton();
|
||||
@@ -309,6 +312,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
return mSearchFeatureProvider.getDatabaseSearchLoader(activity, mQuery);
|
||||
case LOADER_ID_INSTALLED_APPS:
|
||||
return mSearchFeatureProvider.getInstalledAppSearchLoader(activity, mQuery);
|
||||
case LOADER_ID_ACCESSIBILITY_SERVICES:
|
||||
return mSearchFeatureProvider.getAccessibilityServiceResultLoader(activity, mQuery);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -341,8 +346,11 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
mSavedQueryController.loadSavedQueries();
|
||||
} else {
|
||||
final LoaderManager loaderManager = getLoaderManager();
|
||||
loaderManager.initLoader(LOADER_ID_DATABASE, null, this);
|
||||
loaderManager.initLoader(LOADER_ID_INSTALLED_APPS, null, this);
|
||||
loaderManager.initLoader(LOADER_ID_DATABASE, null /* args */, this /* callback */);
|
||||
loaderManager.initLoader(
|
||||
LOADER_ID_INSTALLED_APPS, null /* args */, this /* callback */);
|
||||
loaderManager.initLoader(
|
||||
LOADER_ID_ACCESSIBILITY_SERVICES, null /* args */, this /* callback */);
|
||||
}
|
||||
|
||||
requery();
|
||||
@@ -382,6 +390,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
mUnfinishedLoadersCount.set(NUM_QUERY_LOADERS);
|
||||
loaderManager.restartLoader(LOADER_ID_DATABASE, null /* args */, this /* callback */);
|
||||
loaderManager.restartLoader(LOADER_ID_INSTALLED_APPS, null /* args */, this /* callback */);
|
||||
loaderManager.restartLoader(LOADER_ID_ACCESSIBILITY_SERVICES, null /* args */,
|
||||
this /* callback */);
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
|
@@ -56,6 +56,8 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
|
||||
@VisibleForTesting
|
||||
static final String APP_RESULTS_LOADER_KEY = InstalledAppResultLoader.class.getName();
|
||||
@VisibleForTesting
|
||||
static final String ACCESSIBLITY_LOADER_KEY = AccessibilityServiceResultLoader.class.getName();
|
||||
|
||||
@VisibleForTesting
|
||||
static final int MSG_RANKING_TIMED_OUT = 1;
|
||||
@@ -262,11 +264,16 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
getSortedLoadedResults(DB_RESULTS_LOADER_KEY);
|
||||
List<? extends SearchResult> installedAppResults =
|
||||
getSortedLoadedResults(APP_RESULTS_LOADER_KEY);
|
||||
List<? extends SearchResult> accessibilityResults =
|
||||
getSortedLoadedResults(ACCESSIBLITY_LOADER_KEY);
|
||||
|
||||
int dbSize = databaseResults.size();
|
||||
int appSize = installedAppResults.size();
|
||||
int a11ySize = accessibilityResults.size();
|
||||
|
||||
int dbIndex = 0;
|
||||
int appIndex = 0;
|
||||
int a11yIndex = 0;
|
||||
int rank = SearchResult.TOP_RANK;
|
||||
|
||||
mStaticallyRankedSearchResults.clear();
|
||||
@@ -277,6 +284,9 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
while ((appIndex < appSize) && (installedAppResults.get(appIndex).rank == rank)) {
|
||||
mStaticallyRankedSearchResults.add(installedAppResults.get(appIndex++));
|
||||
}
|
||||
while ((a11yIndex < a11ySize) && (accessibilityResults.get(a11yIndex).rank == rank)) {
|
||||
mStaticallyRankedSearchResults.add(accessibilityResults.get(a11yIndex++));
|
||||
}
|
||||
rank++;
|
||||
}
|
||||
|
||||
@@ -286,6 +296,9 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
while (appIndex < appSize) {
|
||||
mStaticallyRankedSearchResults.add(installedAppResults.get(appIndex++));
|
||||
}
|
||||
while(a11yIndex < a11ySize) {
|
||||
mStaticallyRankedSearchResults.add(accessibilityResults.get(a11yIndex++));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSearchResults() {
|
||||
@@ -318,10 +331,13 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
getUnsortedLoadedResults(DB_RESULTS_LOADER_KEY);
|
||||
List<? extends SearchResult> installedAppResults =
|
||||
getSortedLoadedResults(APP_RESULTS_LOADER_KEY);
|
||||
List<? extends SearchResult> accessibilityResults =
|
||||
getSortedLoadedResults(ACCESSIBLITY_LOADER_KEY);
|
||||
int dbSize = databaseResults.size();
|
||||
int appSize = installedAppResults.size();
|
||||
int a11ySize = accessibilityResults.size();
|
||||
|
||||
final List<SearchResult> asyncRankingResults = new ArrayList<>(dbSize + appSize);
|
||||
final List<SearchResult> asyncRankingResults = new ArrayList<>(dbSize + appSize + a11ySize);
|
||||
TreeSet<SearchResult> dbResultsSortedByScores = new TreeSet<>(
|
||||
new Comparator<SearchResult>() {
|
||||
@Override
|
||||
@@ -339,8 +355,9 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
});
|
||||
dbResultsSortedByScores.addAll(databaseResults);
|
||||
asyncRankingResults.addAll(dbResultsSortedByScores);
|
||||
// App results are not ranked by async ranking and appended at the end of the list.
|
||||
// Other results are not ranked by async ranking and appended at the end of the list.
|
||||
asyncRankingResults.addAll(installedAppResults);
|
||||
asyncRankingResults.addAll(accessibilityResults);
|
||||
return asyncRankingResults;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.search;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.dashboard.SiteMapManager;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class AccessibilityServiceResultLoaderTest {
|
||||
|
||||
private static final String QUERY = "test_query";
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private AccessibilityManager mAccessibilityManager;
|
||||
@Mock
|
||||
private SiteMapManager mSiteMapManager;
|
||||
|
||||
private AccessibilityServiceResultLoader mLoader;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mContext.getSystemService(Context.ACCESSIBILITY_SERVICE))
|
||||
.thenReturn(mAccessibilityManager);
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
|
||||
mLoader = new AccessibilityServiceResultLoader(mContext, QUERY, mSiteMapManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void query_noService_shouldNotReturnAnything() {
|
||||
assertThat(mLoader.loadInBackground()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void query_hasServiceMatchingTitle_shouldReturnResult() {
|
||||
addFakeAccessibilityService();
|
||||
|
||||
List<? extends SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
|
||||
assertThat(results).hasSize(1);
|
||||
|
||||
SearchResult result = results.get(0);
|
||||
assertThat(result.title).isEqualTo(QUERY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void query_serviceDoesNotMatchTitle_shouldReturnResult() {
|
||||
addFakeAccessibilityService();
|
||||
|
||||
mLoader = new AccessibilityServiceResultLoader(mContext,
|
||||
QUERY + "no_match", mSiteMapManager);
|
||||
|
||||
assertThat(mLoader.loadInBackground()).isEmpty();
|
||||
}
|
||||
|
||||
private void addFakeAccessibilityService() {
|
||||
final List<AccessibilityServiceInfo> services = new ArrayList<>();
|
||||
final AccessibilityServiceInfo info = mock(AccessibilityServiceInfo.class);
|
||||
final ResolveInfo resolveInfo = mock(ResolveInfo.class);
|
||||
when(info.getResolveInfo()).thenReturn(resolveInfo);
|
||||
when(resolveInfo.loadIcon(mPackageManager)).thenReturn(new ColorDrawable(Color.BLUE));
|
||||
when(resolveInfo.loadLabel(mPackageManager)).thenReturn(QUERY);
|
||||
resolveInfo.serviceInfo = new ServiceInfo();
|
||||
resolveInfo.serviceInfo.packageName = "pkg";
|
||||
resolveInfo.serviceInfo.name = "class";
|
||||
services.add(info);
|
||||
|
||||
when(mAccessibilityManager.getInstalledAccessibilityServiceList()).thenReturn(services);
|
||||
}
|
||||
}
|
@@ -16,6 +16,19 @@
|
||||
|
||||
package com.android.settings.search;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
@@ -40,15 +53,14 @@ import android.provider.UserDictionary;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.accessibility.AccessibilitySettings;
|
||||
import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
|
||||
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
|
||||
import com.android.settings.inputmethod.VirtualKeyboardFragment;
|
||||
import com.android.settings.language.LanguageAndInputSettings;
|
||||
import com.android.settings.print.PrintSettingsFragment;
|
||||
import com.android.settings.testutils.DatabaseTestUtils;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.ShadowActivityWithLoadManager;
|
||||
import com.android.settings.testutils.shadow.ShadowContextImplWithRegisterReceiver;
|
||||
import com.android.settings.testutils.shadow.ShadowInputManager;
|
||||
@@ -77,19 +89,6 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(
|
||||
manifest = TestConfig.MANIFEST_PATH,
|
||||
@@ -271,67 +270,6 @@ public class DynamicIndexableContentMonitorTest {
|
||||
verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessibilityServicesMonitor() throws Exception {
|
||||
mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
|
||||
|
||||
verifyIncrementalIndexing(AccessibilitySettings.class);
|
||||
|
||||
/*
|
||||
* When an accessibility service package is installed, incremental indexing happen.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
installAccessibilityService(A11Y_PACKAGE_1);
|
||||
|
||||
verifyIncrementalIndexing(AccessibilitySettings.class);
|
||||
|
||||
/*
|
||||
* When another accessibility service package is installed, incremental indexing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
installAccessibilityService(A11Y_PACKAGE_2);
|
||||
|
||||
verifyIncrementalIndexing(AccessibilitySettings.class);
|
||||
|
||||
/*
|
||||
* When an accessibility service is disabled, rebuild indexing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
disableInstalledPackage(A11Y_PACKAGE_1);
|
||||
|
||||
verifyIncrementalIndexing(AccessibilitySettings.class);
|
||||
|
||||
/*
|
||||
* When an accessibility service is enabled, incremental indexing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
enableInstalledPackage(A11Y_PACKAGE_1);
|
||||
|
||||
verifyIncrementalIndexing(AccessibilitySettings.class);
|
||||
|
||||
/*
|
||||
* When an accessibility service package is uninstalled, rebuild indexing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
uninstallAccessibilityService(A11Y_PACKAGE_1);
|
||||
|
||||
verifyIncrementalIndexing(AccessibilitySettings.class);
|
||||
|
||||
/*
|
||||
* When an input method service package is installed, nothing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
installInputMethodService(IME_PACKAGE_1);
|
||||
|
||||
verifyNoIndexing(AccessibilitySettings.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInputMethodServicesMonitor() throws Exception {
|
||||
mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
|
||||
|
@@ -17,6 +17,20 @@
|
||||
|
||||
package com.android.settings.search;
|
||||
|
||||
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
|
||||
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
||||
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.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@@ -26,12 +40,12 @@ import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.applications.PackageManagerWrapper;
|
||||
import com.android.settings.dashboard.SiteMapManager;
|
||||
import com.android.settings.testutils.ApplicationTestUtils;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -50,20 +64,6 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
|
||||
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
||||
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.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class InstalledAppResultLoaderTest {
|
||||
@@ -82,8 +82,8 @@ public class InstalledAppResultLoaderTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
FakeFeatureFactory.setupForTest(mContext);
|
||||
FakeFeatureFactory factory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
|
||||
|
||||
final FakeFeatureFactory factory = FakeFeatureFactory.setupForTest(mContext);
|
||||
when(factory.searchFeatureProvider.getSiteMapManager())
|
||||
.thenReturn(mSiteMapManager);
|
||||
final List<UserInfo> infos = new ArrayList<>();
|
||||
|
@@ -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.search;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class MockAccessiblityLoader extends AccessibilityServiceResultLoader {
|
||||
|
||||
public MockAccessiblityLoader(Context context) {
|
||||
super(context, "test_query", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends SearchResult> loadInBackground() {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDiscardResult(Set<? extends SearchResult> result) {
|
||||
|
||||
}
|
||||
}
|
@@ -81,6 +81,8 @@ public class SearchFragmentTest {
|
||||
private DatabaseResultLoader mDatabaseResultLoader;
|
||||
@Mock
|
||||
private InstalledAppResultLoader mInstalledAppResultLoader;
|
||||
@Mock
|
||||
private AccessibilityServiceResultLoader mAccessibilityServiceResultLoader;
|
||||
|
||||
@Mock
|
||||
private SavedQueryLoader mSavedQueryLoader;
|
||||
@@ -113,6 +115,9 @@ public class SearchFragmentTest {
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getInstalledAppSearchLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mInstalledAppResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getAccessibilityServiceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mAccessibilityServiceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
|
||||
@@ -170,6 +175,9 @@ public class SearchFragmentTest {
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getInstalledAppSearchLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mInstalledAppResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getAccessibilityServiceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mAccessibilityServiceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
|
||||
@@ -225,6 +233,9 @@ public class SearchFragmentTest {
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getInstalledAppSearchLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mInstalledAppResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getAccessibilityServiceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mAccessibilityServiceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
ActivityController<SearchActivity> activityController =
|
||||
@@ -256,6 +267,9 @@ public class SearchFragmentTest {
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getInstalledAppSearchLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mInstalledAppResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getAccessibilityServiceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mAccessibilityServiceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
|
||||
@@ -333,6 +347,9 @@ public class SearchFragmentTest {
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getInstalledAppSearchLoader(any(Context.class), anyString()))
|
||||
.thenReturn(new MockAppLoader(RuntimeEnvironment.application));
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getAccessibilityServiceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(new MockAccessiblityLoader(RuntimeEnvironment.application));
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
ActivityController<SearchActivity> activityController =
|
||||
|
Reference in New Issue
Block a user