Merge "Delete DynamicindexableContentMonitor & all refs to it" into oc-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
5b820431d3
23
res/xml/available_virtual_keyboard.xml
Normal file
23
res/xml/available_virtual_keyboard.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:key="available_virtual_keyboard_page"
|
||||
android:title="@string/add_virtual_keyboard">
|
||||
|
||||
</PreferenceScreen>
|
@@ -59,7 +59,6 @@ import com.android.settings.dashboard.DashboardFeatureProvider;
|
||||
import com.android.settings.dashboard.DashboardSummary;
|
||||
import com.android.settings.development.DevelopmentSettings;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.search.DynamicIndexableContentMonitor;
|
||||
import com.android.settings.search.SearchActivity;
|
||||
import com.android.settings.wfd.WifiDisplaySettings;
|
||||
import com.android.settings.widget.SwitchBar;
|
||||
@@ -76,8 +75,6 @@ public class SettingsActivity extends SettingsDrawerActivity
|
||||
|
||||
private static final String LOG_TAG = "Settings";
|
||||
|
||||
public static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1;
|
||||
|
||||
// Constants for state save/restore
|
||||
private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
|
||||
@VisibleForTesting
|
||||
@@ -183,8 +180,6 @@ public class SettingsActivity extends SettingsDrawerActivity
|
||||
}
|
||||
};
|
||||
|
||||
private DynamicIndexableContentMonitor mDynamicIndexableContentMonitor;
|
||||
|
||||
private SwitchBar mSwitchBar;
|
||||
|
||||
private Button mNextButton;
|
||||
@@ -535,10 +530,6 @@ public class SettingsActivity extends SettingsDrawerActivity
|
||||
mDevelopmentPreferencesListener);
|
||||
|
||||
registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
if (mDynamicIndexableContentMonitor == null) {
|
||||
mDynamicIndexableContentMonitor = new DynamicIndexableContentMonitor();
|
||||
}
|
||||
mDynamicIndexableContentMonitor.register(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
|
||||
|
||||
updateTilesList();
|
||||
}
|
||||
@@ -550,9 +541,6 @@ public class SettingsActivity extends SettingsDrawerActivity
|
||||
mDevelopmentPreferencesListener);
|
||||
mDevelopmentPreferencesListener = null;
|
||||
unregisterReceiver(mBatteryInfoReceiver);
|
||||
if (mDynamicIndexableContentMonitor != null) {
|
||||
mDynamicIndexableContentMonitor.unregister(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -21,7 +21,6 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -31,17 +30,15 @@ import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.InputMethodSubtype;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
|
||||
import com.android.settingslib.inputmethod.InputMethodPreference;
|
||||
import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper;
|
||||
@@ -60,10 +57,9 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle bundle, String s) {
|
||||
addPreferencesFromResource(R.xml.available_virtual_keyboard);
|
||||
Activity activity = getActivity();
|
||||
PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(activity);
|
||||
screen.setTitle(activity.getString(R.string.available_virtual_keyboard_category));
|
||||
setPreferenceScreen(screen);
|
||||
|
||||
mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity);
|
||||
mImm = activity.getSystemService(InputMethodManager.class);
|
||||
mDpm = activity.getSystemService(DevicePolicyManager.class);
|
||||
@@ -105,7 +101,7 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
|
||||
}
|
||||
try {
|
||||
return packageManager.getDrawable(packageName, resId, applicationInfo);
|
||||
} catch (Exception e){
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -172,48 +168,16 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
|
||||
}
|
||||
}
|
||||
|
||||
private static List<InputMethodSubtype> getAllSubtypesOf(final InputMethodInfo imi) {
|
||||
final int subtypeCount = imi.getSubtypeCount();
|
||||
final List<InputMethodSubtype> allSubtypes = new ArrayList<>(subtypeCount);
|
||||
for (int index = 0; index < subtypeCount; index++) {
|
||||
allSubtypes.add(imi.getSubtypeAt(index));
|
||||
}
|
||||
return allSubtypes;
|
||||
}
|
||||
|
||||
static List<SearchIndexableRaw> buildSearchIndexOfInputMethods(final Context context,
|
||||
final List<InputMethodInfo> inputMethods, final String screenTitle) {
|
||||
final List<SearchIndexableRaw> indexes = new ArrayList<>();
|
||||
for (int i = 0; i < inputMethods.size(); i++) {
|
||||
final InputMethodInfo imi = inputMethods.get(i);
|
||||
final ServiceInfo serviceInfo = imi.getServiceInfo();
|
||||
final SearchIndexableRaw index = new SearchIndexableRaw(context);
|
||||
index.key = new ComponentName(serviceInfo.packageName, serviceInfo.name)
|
||||
.flattenToString();
|
||||
index.title = imi.loadLabel(context.getPackageManager()).toString();
|
||||
index.summaryOn = index.summaryOff = InputMethodAndSubtypeUtil
|
||||
.getSubtypeLocaleNameListAsSentence(getAllSubtypesOf(imi), context, imi);
|
||||
index.screenTitle = screenTitle;
|
||||
indexes.add(index);
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
|
||||
final InputMethodManager imm = context.getSystemService(InputMethodManager.class);
|
||||
final List<InputMethodInfo> enabledInputMethods = imm.getEnabledInputMethodList();
|
||||
final List<InputMethodInfo> disabledInputMethods = new ArrayList<>();
|
||||
for (final InputMethodInfo imi : imm.getInputMethodList()) {
|
||||
if (!enabledInputMethods.contains(imi)) {
|
||||
disabledInputMethods.add(imi);
|
||||
@Override
|
||||
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
|
||||
boolean enabled) {
|
||||
List<SearchIndexableResource> res = new ArrayList<>();
|
||||
SearchIndexableResource index = new SearchIndexableResource(context);
|
||||
index.xmlResId = R.xml.available_virtual_keyboard;
|
||||
res.add(index);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
final String screenTitle = context.getString(
|
||||
R.string.available_virtual_keyboard_category);
|
||||
return buildSearchIndexOfInputMethods(context, disabledInputMethods, screenTitle);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@ import android.hardware.input.KeyboardLayout;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.support.v14.preference.SwitchPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
@@ -51,11 +52,11 @@ import com.android.settings.Settings;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -288,6 +289,7 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
|
||||
final PhysicalKeyboardFragment mPhysicalKeyboardFragment;
|
||||
@NonNull
|
||||
final List<HardKeyboardDeviceInfo> mHardKeyboards;
|
||||
|
||||
public Callbacks(
|
||||
@NonNull Context context,
|
||||
@NonNull PhysicalKeyboardFragment physicalKeyboardFragment,
|
||||
@@ -532,43 +534,14 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
|
||||
}
|
||||
}
|
||||
|
||||
public static List<InputDevice> getPhysicalFullKeyboards() {
|
||||
List<InputDevice> keyboards = null;
|
||||
for (final int deviceId : InputDevice.getDeviceIds()) {
|
||||
final InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
|
||||
if (keyboards == null) keyboards = new ArrayList<>();
|
||||
keyboards.add(device);
|
||||
}
|
||||
}
|
||||
return (keyboards == null) ? Collections.emptyList() : keyboards;
|
||||
}
|
||||
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
|
||||
final InputManager inputManager = (InputManager) context.getSystemService(
|
||||
Context.INPUT_SERVICE);
|
||||
final String screenTitle = context.getString(R.string.physical_keyboard_title);
|
||||
final List<SearchIndexableRaw> indexes = new ArrayList<>();
|
||||
for (final InputDevice device : getPhysicalFullKeyboards()) {
|
||||
final String keyboardLayoutDescriptor = inputManager
|
||||
.getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
|
||||
final KeyboardLayout keyboardLayout = (keyboardLayoutDescriptor != null)
|
||||
? inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
|
||||
final String summary = (keyboardLayout != null)
|
||||
? keyboardLayout.toString()
|
||||
: context.getString(R.string.keyboard_layout_default_label);
|
||||
final SearchIndexableRaw index = new SearchIndexableRaw(context);
|
||||
index.key = device.getName();
|
||||
index.title = device.getName();
|
||||
index.summaryOn = summary;
|
||||
index.summaryOff = summary;
|
||||
index.screenTitle = screenTitle;
|
||||
indexes.add(index);
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public List<SearchIndexableResource> getXmlResourcesToIndex(
|
||||
Context context, boolean enabled) {
|
||||
final SearchIndexableResource sir = new SearchIndexableResource(context);
|
||||
sir.xmlResId = R.xml.physical_keyboard_settings;
|
||||
return Arrays.asList(sir);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
@@ -33,14 +34,12 @@ import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
|
||||
import com.android.settingslib.inputmethod.InputMethodPreference;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class VirtualKeyboardFragment extends SettingsPreferenceFragment implements Indexable {
|
||||
@@ -121,13 +120,19 @@ public final class VirtualKeyboardFragment extends SettingsPreferenceFragment im
|
||||
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
|
||||
final InputMethodManager imm = context.getSystemService(InputMethodManager.class);
|
||||
final List<InputMethodInfo> enabledInputMethods = imm.getEnabledInputMethodList();
|
||||
final String screenTitle = context.getString(R.string.virtual_keyboard_category);
|
||||
return AvailableVirtualKeyboardFragment
|
||||
.buildSearchIndexOfInputMethods(context, enabledInputMethods, screenTitle);
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public List<SearchIndexableResource> getXmlResourcesToIndex(
|
||||
Context context, boolean enabled) {
|
||||
final SearchIndexableResource sir = new SearchIndexableResource(context);
|
||||
sir.xmlResId = R.xml.virtual_keyboard_settings;
|
||||
return Arrays.asList(sir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getNonIndexableKeys(Context context) {
|
||||
final List<String> keys = super.getNonIndexableKeys(context);
|
||||
keys.add("add_virtual_keyboard_screen");
|
||||
return keys;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -52,6 +52,7 @@ public class LanguageAndInputSettings extends DashboardFragment {
|
||||
private static final String TAG = "LangAndInputSettings";
|
||||
|
||||
private static final String KEY_TEXT_TO_SPEECH = "tts_settings_summary";
|
||||
private static final String KEY_PHYSICAL_KEYBOARD = "physical_keyboard_pref";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -174,6 +175,7 @@ public class LanguageAndInputSettings extends DashboardFragment {
|
||||
List<String> keys = super.getNonIndexableKeys(context);
|
||||
// Duplicates in summary and details pages.
|
||||
keys.add(KEY_TEXT_TO_SPEECH);
|
||||
keys.add(KEY_PHYSICAL_KEYBOARD);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
@@ -1,415 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.accessibilityservice.AccessibilityService;
|
||||
import android.app.Activity;
|
||||
import android.app.LoaderManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.Loader;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.print.PrintManager;
|
||||
import android.print.PrintServicesLoader;
|
||||
import android.printservice.PrintServiceInfo;
|
||||
import android.provider.Settings;
|
||||
import android.provider.UserDictionary;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.Log;
|
||||
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.inputmethod.AvailableVirtualKeyboardFragment;
|
||||
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
|
||||
import com.android.settings.inputmethod.VirtualKeyboardFragment;
|
||||
import com.android.settings.language.LanguageAndInputSettings;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.print.PrintSettingsFragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class DynamicIndexableContentMonitor implements
|
||||
LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
|
||||
// Shorten the class name because log TAG can be at most 23 chars.
|
||||
private static final String TAG = "DynamicContentMonitor";
|
||||
|
||||
private static final long DELAY_PROCESS_PACKAGE_CHANGE = 2000;
|
||||
// A PackageMonitor shared among Settings activities.
|
||||
private static final PackageChangeMonitor PACKAGE_CHANGE_MONITOR = new PackageChangeMonitor();
|
||||
|
||||
// Null if not initialized.
|
||||
@Nullable private DatabaseIndexingManager mIndexManager;
|
||||
private Context mContext;
|
||||
private boolean mHasFeaturePrinting;
|
||||
|
||||
@VisibleForTesting
|
||||
static Intent getAccessibilityServiceIntent(String packageName) {
|
||||
final Intent intent = new Intent(AccessibilityService.SERVICE_INTERFACE);
|
||||
intent.setPackage(packageName);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static Intent getIMEServiceIntent(String packageName) {
|
||||
final Intent intent = new Intent(InputMethod.SERVICE_INTERFACE);
|
||||
intent.setPackage(packageName);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static void resetForTesting() {
|
||||
InputDevicesMonitor.getInstance().resetForTesting();
|
||||
InputMethodServicesMonitor.getInstance().resetForTesting();
|
||||
}
|
||||
|
||||
/**
|
||||
* This instance holds a set of content monitor singleton objects.
|
||||
*
|
||||
* This object is created every time a sub-settings that extends {@code SettingsActivity}
|
||||
* is created.
|
||||
*/
|
||||
public DynamicIndexableContentMonitor() {}
|
||||
|
||||
/**
|
||||
* Creates and initializes a set of content monitor singleton objects if not yet exist.
|
||||
* Also starts loading the list of print services.
|
||||
* <code>mIndex</code> has non-null value after successfully initialized.
|
||||
*
|
||||
* @param activity used to get {@link LoaderManager}.
|
||||
* @param loaderId id for loading print services.
|
||||
*/
|
||||
public void register(Activity activity, int loaderId) {
|
||||
final boolean isUserUnlocked = activity
|
||||
.getSystemService(UserManager.class)
|
||||
.isUserUnlocked();
|
||||
register(activity, loaderId, FeatureFactory.getFactory(activity)
|
||||
.getSearchFeatureProvider().getIndexingManager(activity), isUserUnlocked);
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing to inject {@link DatabaseIndexingManager} object.
|
||||
* Also because currently Robolectric doesn't support API 24, we can not test code that calls
|
||||
* {@link UserManager#isUserUnlocked()}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void register(Activity activity, int loaderId, DatabaseIndexingManager indexManager,
|
||||
boolean isUserUnlocked) {
|
||||
if (!isUserUnlocked) {
|
||||
Log.w(TAG, "Skipping content monitoring because user is locked");
|
||||
return;
|
||||
}
|
||||
final Context context = activity.getApplicationContext();
|
||||
mContext = context;
|
||||
mIndexManager = indexManager;
|
||||
|
||||
PACKAGE_CHANGE_MONITOR.registerMonitor(context);
|
||||
mHasFeaturePrinting = context.getPackageManager()
|
||||
.hasSystemFeature(PackageManager.FEATURE_PRINTING);
|
||||
if (mHasFeaturePrinting) {
|
||||
activity.getLoaderManager().initLoader(loaderId, null /* args */, this /* callbacks */);
|
||||
}
|
||||
|
||||
// Watch for input device changes.
|
||||
InputDevicesMonitor.getInstance().initialize(context, mIndexManager);
|
||||
|
||||
// Start tracking packages.
|
||||
InputMethodServicesMonitor.getInstance().initialize(context, mIndexManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts loading the list of print services.
|
||||
* Note that a set of content monitor singletons keep alive while Settings app is running.
|
||||
*
|
||||
* @param activity user to get {@link LoaderManager}.
|
||||
* @param loaderId id for loading print services.
|
||||
*/
|
||||
public void unregister(Activity activity, int loaderId) {
|
||||
if (mIndexManager == null) return;
|
||||
|
||||
PACKAGE_CHANGE_MONITOR.unregisterMonitor();
|
||||
if (mHasFeaturePrinting) {
|
||||
activity.getLoaderManager().destroyLoader(loaderId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
|
||||
return new PrintServicesLoader(
|
||||
(PrintManager) mContext.getSystemService(Context.PRINT_SERVICE), mContext,
|
||||
PrintManager.ALL_SERVICES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
|
||||
List<PrintServiceInfo> services) {
|
||||
mIndexManager.updateFromClassNameResource(PrintSettingsFragment.class.getName(),
|
||||
true /* includeInSearchResults */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
// A singleton that monitors input devices changes and updates indexes of physical keyboards.
|
||||
private static class InputDevicesMonitor implements InputManager.InputDeviceListener {
|
||||
|
||||
// Null if not initialized.
|
||||
@Nullable private DatabaseIndexingManager mIndexManager;
|
||||
private InputManager mInputManager;
|
||||
|
||||
private InputDevicesMonitor() {}
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final InputDevicesMonitor INSTANCE = new InputDevicesMonitor();
|
||||
}
|
||||
|
||||
static InputDevicesMonitor getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
synchronized void resetForTesting() {
|
||||
if (mIndexManager != null) {
|
||||
mInputManager.unregisterInputDeviceListener(this /* listener */);
|
||||
}
|
||||
mIndexManager = null;
|
||||
}
|
||||
|
||||
synchronized void initialize(Context context, DatabaseIndexingManager indexManager) {
|
||||
if (mIndexManager != null) return;
|
||||
mIndexManager = indexManager;
|
||||
mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
|
||||
buildIndex();
|
||||
|
||||
// Watch for input device changes.
|
||||
mInputManager.registerInputDeviceListener(this /* listener */, null /* handler */);
|
||||
}
|
||||
|
||||
private void buildIndex() {
|
||||
mIndexManager.updateFromClassNameResource(PhysicalKeyboardFragment.class.getName(),
|
||||
true /* includeInSearchResults */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceAdded(int deviceId) {
|
||||
buildIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceRemoved(int deviceId) {
|
||||
buildIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceChanged(int deviceId) {
|
||||
buildIndex();
|
||||
}
|
||||
}
|
||||
|
||||
// A singleton that monitors package installing, uninstalling, enabling, and disabling.
|
||||
// Then updates indexes of accessibility services and input methods.
|
||||
private static class PackageChangeMonitor extends PackageMonitor {
|
||||
private static final String TAG = PackageChangeMonitor.class.getSimpleName();
|
||||
|
||||
// Null if not initialized. Guarded by {@link #mLock}.
|
||||
@Nullable private PackageManager mPackageManager;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
public void registerMonitor(Context context) {
|
||||
synchronized (mLock) {
|
||||
if (mPackageManager != null) {
|
||||
return;
|
||||
}
|
||||
mPackageManager = context.getPackageManager();
|
||||
|
||||
// Start tracking packages. Use background thread for monitoring. Note that no need
|
||||
// to unregister this monitor. This should be alive while Settings app is running.
|
||||
super.register(context, null /* thread */, UserHandle.CURRENT, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterMonitor() {
|
||||
synchronized (mLock) {
|
||||
if (mPackageManager == null) {
|
||||
return;
|
||||
}
|
||||
super.unregister();
|
||||
mPackageManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Covers installed, appeared external storage with the package, upgraded.
|
||||
@Override
|
||||
public void onPackageAppeared(String packageName, int reason) {
|
||||
postPackageAvailable(packageName);
|
||||
}
|
||||
|
||||
// Covers uninstalled, removed external storage with the package.
|
||||
@Override
|
||||
public void onPackageDisappeared(String packageName, int reason) {
|
||||
postPackageUnavailable(packageName);
|
||||
}
|
||||
|
||||
// Covers enabled, disabled.
|
||||
@Override
|
||||
public void onPackageModified(String packageName) {
|
||||
try {
|
||||
final int state = mPackageManager.getApplicationEnabledSetting(packageName);
|
||||
if (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|
||||
|| state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
|
||||
postPackageAvailable(packageName);
|
||||
} else {
|
||||
postPackageUnavailable(packageName);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, "Package does not exist: " + packageName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void postPackageAvailable(final String packageName) {
|
||||
getRegisteredHandler().postDelayed(() -> {
|
||||
InputMethodServicesMonitor.getInstance().onPackageAvailable(packageName);
|
||||
}, DELAY_PROCESS_PACKAGE_CHANGE);
|
||||
}
|
||||
|
||||
private void postPackageUnavailable(final String packageName) {
|
||||
getRegisteredHandler().postDelayed(() -> {
|
||||
InputMethodServicesMonitor.getInstance().onPackageUnavailable(packageName);
|
||||
}, DELAY_PROCESS_PACKAGE_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
private static final Uri ENABLED_INPUT_METHODS_CONTENT_URI =
|
||||
Settings.Secure.getUriFor(Settings.Secure.ENABLED_INPUT_METHODS);
|
||||
|
||||
// Null if not initialized.
|
||||
@Nullable private DatabaseIndexingManager mIndexManager;
|
||||
private PackageManager mPackageManager;
|
||||
private ContentResolver mContentResolver;
|
||||
private final List<String> mInputMethodServices = new ArrayList<>();
|
||||
|
||||
private InputMethodServicesMonitor() {
|
||||
// No need for handler because {@link #onChange(boolean,Uri)} is short and quick.
|
||||
super(null /* handler */);
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final InputMethodServicesMonitor INSTANCE =
|
||||
new InputMethodServicesMonitor();
|
||||
}
|
||||
|
||||
static InputMethodServicesMonitor getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
synchronized void resetForTesting() {
|
||||
if (mIndexManager != null) {
|
||||
mContentResolver.unregisterContentObserver(this /* observer */);
|
||||
}
|
||||
mIndexManager = null;
|
||||
}
|
||||
|
||||
synchronized void initialize(Context context, DatabaseIndexingManager indexManager) {
|
||||
final boolean hasFeatureIme = context.getPackageManager()
|
||||
.hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS);
|
||||
if (!hasFeatureIme) return;
|
||||
|
||||
if (mIndexManager != null) return;
|
||||
mIndexManager = indexManager;
|
||||
mPackageManager = context.getPackageManager();
|
||||
mContentResolver = context.getContentResolver();
|
||||
mInputMethodServices.clear();
|
||||
// Build index of {@link UserDictionary}.
|
||||
buildIndex(LanguageAndInputSettings.class);
|
||||
// Build index of IMEs.
|
||||
buildIndex(VirtualKeyboardFragment.class);
|
||||
buildIndex(AvailableVirtualKeyboardFragment.class);
|
||||
|
||||
// Cache IME service packages to know when they go away.
|
||||
final InputMethodManager inputMethodManager = (InputMethodManager) context
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
for (final InputMethodInfo inputMethod : inputMethodManager.getInputMethodList()) {
|
||||
ServiceInfo serviceInfo = inputMethod.getServiceInfo();
|
||||
if (serviceInfo != null) {
|
||||
mInputMethodServices.add(serviceInfo.packageName);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implements by JobScheduler with TriggerContentUri parameters.
|
||||
// Watch for related content URIs.
|
||||
mContentResolver.registerContentObserver(UserDictionary.Words.CONTENT_URI,
|
||||
true /* notifyForDescendants */, this /* observer */);
|
||||
// Watch for changing enabled IMEs.
|
||||
mContentResolver.registerContentObserver(ENABLED_INPUT_METHODS_CONTENT_URI,
|
||||
false /* notifyForDescendants */, this /* observer */);
|
||||
}
|
||||
|
||||
private void buildIndex(Class<?> indexClass) {
|
||||
mIndexManager.updateFromClassNameResource(indexClass.getName(),
|
||||
true /* includeInSearchResults */);
|
||||
}
|
||||
|
||||
synchronized void onPackageAvailable(String packageName) {
|
||||
if (mIndexManager == null) return;
|
||||
if (mInputMethodServices.contains(packageName)) return;
|
||||
|
||||
final Intent intent = getIMEServiceIntent(packageName);
|
||||
final List<ResolveInfo> services = mPackageManager
|
||||
.queryIntentServices(intent, 0 /* flags */);
|
||||
if (services == null || services.isEmpty()) return;
|
||||
mInputMethodServices.add(packageName);
|
||||
buildIndex(VirtualKeyboardFragment.class);
|
||||
buildIndex(AvailableVirtualKeyboardFragment.class);
|
||||
}
|
||||
|
||||
synchronized void onPackageUnavailable(String packageName) {
|
||||
if (mIndexManager == null) return;
|
||||
if (!mInputMethodServices.remove(packageName)) return;
|
||||
buildIndex(VirtualKeyboardFragment.class);
|
||||
buildIndex(AvailableVirtualKeyboardFragment.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
if (ENABLED_INPUT_METHODS_CONTENT_URI.equals(uri)) {
|
||||
buildIndex(VirtualKeyboardFragment.class);
|
||||
buildIndex(AvailableVirtualKeyboardFragment.class);
|
||||
} else if (UserDictionary.Words.CONTENT_URI.equals(uri)) {
|
||||
buildIndex(LanguageAndInputSettings.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
200
src/com/android/settings/search/InputDeviceResultLoader.java
Normal file
200
src/com/android/settings/search/InputDeviceResultLoader.java
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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 android.content.Context.INPUT_METHOD_SERVICE;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.hardware.input.KeyboardLayout;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.view.InputDevice;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.InputMethodSubtype;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.SiteMapManager;
|
||||
import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
|
||||
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
|
||||
import com.android.settings.utils.AsyncLoader;
|
||||
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Search result for input devices (physical/virtual keyboard, game controllers, etc)
|
||||
*/
|
||||
public class InputDeviceResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
|
||||
private static final int NAME_NO_MATCH = -1;
|
||||
|
||||
@VisibleForTesting
|
||||
static final String PHYSICAL_KEYBOARD_FRAGMENT = PhysicalKeyboardFragment.class.getName();
|
||||
@VisibleForTesting
|
||||
static final String VIRTUAL_KEYBOARD_FRAGMENT =
|
||||
AvailableVirtualKeyboardFragment.class.getName();
|
||||
|
||||
private final SiteMapManager mSiteMapManager;
|
||||
private final InputManager mInputManager;
|
||||
private final InputMethodManager mImm;
|
||||
private final PackageManager mPackageManager;
|
||||
@VisibleForTesting
|
||||
final String mQuery;
|
||||
|
||||
private List<String> mPhysicalKeyboardBreadcrumb;
|
||||
private List<String> mVirtualKeyboardBreadcrumb;
|
||||
|
||||
public InputDeviceResultLoader(Context context, String query, SiteMapManager mapManager) {
|
||||
super(context);
|
||||
mQuery = query;
|
||||
mSiteMapManager = mapManager;
|
||||
mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
|
||||
mImm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
|
||||
mPackageManager = context.getPackageManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDiscardResult(Set<? extends SearchResult> result) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends SearchResult> loadInBackground() {
|
||||
final Set<SearchResult> results = new HashSet<>();
|
||||
results.addAll(buildPhysicalKeyboardSearchResults());
|
||||
results.addAll(buildVirtualKeyboardSearchResults());
|
||||
return results;
|
||||
}
|
||||
|
||||
private Set<SearchResult> buildPhysicalKeyboardSearchResults() {
|
||||
final Set<SearchResult> results = new HashSet<>();
|
||||
final Context context = getContext();
|
||||
final String screenTitle = context.getString(R.string.physical_keyboard_title);
|
||||
|
||||
for (final InputDevice device : getPhysicalFullKeyboards()) {
|
||||
final String deviceName = device.getName();
|
||||
final int wordDiff = InstalledAppResultLoader.getWordDifference(deviceName, mQuery);
|
||||
if (wordDiff == NAME_NO_MATCH) {
|
||||
continue;
|
||||
}
|
||||
final String keyboardLayoutDescriptor = mInputManager
|
||||
.getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
|
||||
final KeyboardLayout keyboardLayout = (keyboardLayoutDescriptor != null)
|
||||
? mInputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
|
||||
final String summary = (keyboardLayout != null)
|
||||
? keyboardLayout.toString()
|
||||
: context.getString(R.string.keyboard_layout_default_label);
|
||||
final String key = deviceName;
|
||||
|
||||
final Intent intent = DatabaseIndexingUtils.buildSubsettingIntent(context,
|
||||
PHYSICAL_KEYBOARD_FRAGMENT, key, screenTitle);
|
||||
results.add(new SearchResult.Builder()
|
||||
.setTitle(deviceName)
|
||||
.setPayload(new ResultPayload(intent))
|
||||
.setStableId(Objects.hash(PHYSICAL_KEYBOARD_FRAGMENT, key))
|
||||
.setSummary(summary)
|
||||
.setRank(wordDiff)
|
||||
.addBreadcrumbs(getPhysicalKeyboardBreadCrumb())
|
||||
.build());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private Set<SearchResult> buildVirtualKeyboardSearchResults() {
|
||||
final Set<SearchResult> results = new HashSet<>();
|
||||
final Context context = getContext();
|
||||
final String screenTitle = context.getString(R.string.add_virtual_keyboard);
|
||||
final List<InputMethodInfo> inputMethods = mImm.getInputMethodList();
|
||||
for (InputMethodInfo info : inputMethods) {
|
||||
final String title = info.loadLabel(mPackageManager).toString();
|
||||
final String summary = InputMethodAndSubtypeUtil
|
||||
.getSubtypeLocaleNameListAsSentence(getAllSubtypesOf(info), context, info);
|
||||
int wordDiff = InstalledAppResultLoader.getWordDifference(title, mQuery);
|
||||
if (wordDiff == NAME_NO_MATCH) {
|
||||
wordDiff = InstalledAppResultLoader.getWordDifference(summary, mQuery);
|
||||
}
|
||||
if (wordDiff == NAME_NO_MATCH) {
|
||||
continue;
|
||||
}
|
||||
final ServiceInfo serviceInfo = info.getServiceInfo();
|
||||
final String key = new ComponentName(serviceInfo.packageName, serviceInfo.name)
|
||||
.flattenToString();
|
||||
final Intent intent = DatabaseIndexingUtils.buildSubsettingIntent(context,
|
||||
VIRTUAL_KEYBOARD_FRAGMENT, key, screenTitle);
|
||||
results.add(new SearchResult.Builder()
|
||||
.setTitle(title)
|
||||
.setSummary(summary)
|
||||
.setRank(wordDiff)
|
||||
.setStableId(Objects.hash(VIRTUAL_KEYBOARD_FRAGMENT, key))
|
||||
.addBreadcrumbs(getVirtualKeyboardBreadCrumb())
|
||||
.setPayload(new ResultPayload(intent))
|
||||
.build());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<String> getPhysicalKeyboardBreadCrumb() {
|
||||
if (mPhysicalKeyboardBreadcrumb == null || mPhysicalKeyboardBreadcrumb.isEmpty()) {
|
||||
final Context context = getContext();
|
||||
mPhysicalKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
|
||||
context, PHYSICAL_KEYBOARD_FRAGMENT,
|
||||
context.getString(R.string.physical_keyboard_title));
|
||||
}
|
||||
return mPhysicalKeyboardBreadcrumb;
|
||||
}
|
||||
|
||||
|
||||
private List<String> getVirtualKeyboardBreadCrumb() {
|
||||
if (mVirtualKeyboardBreadcrumb == null || mVirtualKeyboardBreadcrumb.isEmpty()) {
|
||||
final Context context = getContext();
|
||||
mVirtualKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
|
||||
context, VIRTUAL_KEYBOARD_FRAGMENT,
|
||||
context.getString(R.string.add_virtual_keyboard));
|
||||
}
|
||||
return mVirtualKeyboardBreadcrumb;
|
||||
}
|
||||
|
||||
private List<InputDevice> getPhysicalFullKeyboards() {
|
||||
final List<InputDevice> keyboards = new ArrayList<>();
|
||||
final int[] deviceIds = InputDevice.getDeviceIds();
|
||||
if (deviceIds != null) {
|
||||
for (int deviceId : deviceIds) {
|
||||
final InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
|
||||
keyboards.add(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
return keyboards;
|
||||
}
|
||||
|
||||
private static List<InputMethodSubtype> getAllSubtypesOf(final InputMethodInfo imi) {
|
||||
final int subtypeCount = imi.getSubtypeCount();
|
||||
final List<InputMethodSubtype> allSubtypes = new ArrayList<>(subtypeCount);
|
||||
for (int index = 0; index < subtypeCount; index++) {
|
||||
allSubtypes.add(imi.getSubtypeAt(index));
|
||||
}
|
||||
return allSubtypes;
|
||||
}
|
||||
}
|
@@ -48,6 +48,11 @@ public interface SearchFeatureProvider {
|
||||
AccessibilityServiceResultLoader getAccessibilityServiceResultLoader(Context context,
|
||||
String query);
|
||||
|
||||
/**
|
||||
* Returns a new loader to search input devices.
|
||||
*/
|
||||
InputDeviceResultLoader getInputDeviceResultLoader(Context context, String query);
|
||||
|
||||
/**
|
||||
* Returns a new loader to get all recently saved queries search terms.
|
||||
*/
|
||||
|
@@ -61,6 +61,11 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
||||
getSiteMapManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputDeviceResultLoader getInputDeviceResultLoader(Context context, String query) {
|
||||
return new InputDeviceResultLoader(context, cleanQuery(query), getSiteMapManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedQueryLoader getSavedQueryLoader(Context context) {
|
||||
return new SavedQueryLoader(context);
|
||||
|
@@ -84,8 +84,10 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
static final int LOADER_ID_INSTALLED_APPS = 2;
|
||||
@VisibleForTesting
|
||||
static final int LOADER_ID_ACCESSIBILITY_SERVICES = 3;
|
||||
@VisibleForTesting
|
||||
static final int LOADER_ID_INPUT_DEVICES = 4;
|
||||
|
||||
private static final int NUM_QUERY_LOADERS = 3;
|
||||
private static final int NUM_QUERY_LOADERS = 4;
|
||||
|
||||
@VisibleForTesting
|
||||
AtomicInteger mUnfinishedLoadersCount = new AtomicInteger(NUM_QUERY_LOADERS);
|
||||
@@ -284,6 +286,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
loaderManager.destroyLoader(LOADER_ID_DATABASE);
|
||||
loaderManager.destroyLoader(LOADER_ID_INSTALLED_APPS);
|
||||
loaderManager.destroyLoader(LOADER_ID_ACCESSIBILITY_SERVICES);
|
||||
loaderManager.destroyLoader(LOADER_ID_INPUT_DEVICES);
|
||||
mShowingSavedQuery = true;
|
||||
mSavedQueryController.loadSavedQueries();
|
||||
mSearchFeatureProvider.hideFeedbackButton();
|
||||
@@ -314,6 +317,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
return mSearchFeatureProvider.getInstalledAppSearchLoader(activity, mQuery);
|
||||
case LOADER_ID_ACCESSIBILITY_SERVICES:
|
||||
return mSearchFeatureProvider.getAccessibilityServiceResultLoader(activity, mQuery);
|
||||
case LOADER_ID_INPUT_DEVICES:
|
||||
return mSearchFeatureProvider.getInputDeviceResultLoader(activity, mQuery);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -351,6 +356,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
LOADER_ID_INSTALLED_APPS, null /* args */, this /* callback */);
|
||||
loaderManager.initLoader(
|
||||
LOADER_ID_ACCESSIBILITY_SERVICES, null /* args */, this /* callback */);
|
||||
loaderManager.initLoader(
|
||||
LOADER_ID_INPUT_DEVICES, null /* args */, this /* callback */);
|
||||
}
|
||||
|
||||
requery();
|
||||
@@ -392,6 +399,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
|
||||
loaderManager.restartLoader(LOADER_ID_INSTALLED_APPS, null /* args */, this /* callback */);
|
||||
loaderManager.restartLoader(LOADER_ID_ACCESSIBILITY_SERVICES, null /* args */,
|
||||
this /* callback */);
|
||||
loaderManager.restartLoader(LOADER_ID_INPUT_DEVICES, null /* args */,
|
||||
this /* callback */);
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
|
@@ -57,7 +57,9 @@ 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();
|
||||
static final String ACCESSIBILITY_LOADER_KEY = AccessibilityServiceResultLoader.class.getName();
|
||||
@VisibleForTesting
|
||||
static final String INPUT_DEVICE_LOADER_KEY = InputDeviceResultLoader.class.getName();
|
||||
|
||||
@VisibleForTesting
|
||||
static final int MSG_RANKING_TIMED_OUT = 1;
|
||||
@@ -265,17 +267,21 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
List<? extends SearchResult> installedAppResults =
|
||||
getSortedLoadedResults(APP_RESULTS_LOADER_KEY);
|
||||
List<? extends SearchResult> accessibilityResults =
|
||||
getSortedLoadedResults(ACCESSIBLITY_LOADER_KEY);
|
||||
getSortedLoadedResults(ACCESSIBILITY_LOADER_KEY);
|
||||
List<? extends SearchResult> inputDeviceResults =
|
||||
getSortedLoadedResults(INPUT_DEVICE_LOADER_KEY);
|
||||
|
||||
int dbSize = databaseResults.size();
|
||||
int appSize = installedAppResults.size();
|
||||
int a11ySize = accessibilityResults.size();
|
||||
|
||||
int inputDeviceSize = inputDeviceResults.size();
|
||||
int dbIndex = 0;
|
||||
int appIndex = 0;
|
||||
int a11yIndex = 0;
|
||||
int inputDeviceIndex = 0;
|
||||
int rank = SearchResult.TOP_RANK;
|
||||
|
||||
// TODO: We need a helper method to do k-way merge.
|
||||
mStaticallyRankedSearchResults.clear();
|
||||
while (rank <= SearchResult.BOTTOM_RANK) {
|
||||
while ((dbIndex < dbSize) && (databaseResults.get(dbIndex).rank == rank)) {
|
||||
@@ -287,6 +293,10 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
while ((a11yIndex < a11ySize) && (accessibilityResults.get(a11yIndex).rank == rank)) {
|
||||
mStaticallyRankedSearchResults.add(accessibilityResults.get(a11yIndex++));
|
||||
}
|
||||
while (inputDeviceIndex < inputDeviceSize
|
||||
&& inputDeviceResults.get(inputDeviceIndex).rank == rank) {
|
||||
mStaticallyRankedSearchResults.add(inputDeviceResults.get(inputDeviceIndex++));
|
||||
}
|
||||
rank++;
|
||||
}
|
||||
|
||||
@@ -299,6 +309,9 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
while(a11yIndex < a11ySize) {
|
||||
mStaticallyRankedSearchResults.add(accessibilityResults.get(a11yIndex++));
|
||||
}
|
||||
while (inputDeviceIndex < inputDeviceSize) {
|
||||
mStaticallyRankedSearchResults.add(inputDeviceResults.get(inputDeviceIndex++));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSearchResults() {
|
||||
@@ -332,12 +345,16 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
List<? extends SearchResult> installedAppResults =
|
||||
getSortedLoadedResults(APP_RESULTS_LOADER_KEY);
|
||||
List<? extends SearchResult> accessibilityResults =
|
||||
getSortedLoadedResults(ACCESSIBLITY_LOADER_KEY);
|
||||
getSortedLoadedResults(ACCESSIBILITY_LOADER_KEY);
|
||||
List<? extends SearchResult> inputDeviceResults =
|
||||
getSortedLoadedResults(INPUT_DEVICE_LOADER_KEY);
|
||||
int dbSize = databaseResults.size();
|
||||
int appSize = installedAppResults.size();
|
||||
int a11ySize = accessibilityResults.size();
|
||||
int inputDeviceSize = inputDeviceResults.size();
|
||||
|
||||
final List<SearchResult> asyncRankingResults = new ArrayList<>(dbSize + appSize + a11ySize);
|
||||
final List<SearchResult> asyncRankingResults = new ArrayList<>(
|
||||
dbSize + appSize + a11ySize + inputDeviceSize);
|
||||
TreeSet<SearchResult> dbResultsSortedByScores = new TreeSet<>(
|
||||
new Comparator<SearchResult>() {
|
||||
@Override
|
||||
@@ -358,13 +375,13 @@ public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
|
||||
// Other results are not ranked by async ranking and appended at the end of the list.
|
||||
asyncRankingResults.addAll(installedAppResults);
|
||||
asyncRankingResults.addAll(accessibilityResults);
|
||||
asyncRankingResults.addAll(inputDeviceResults);
|
||||
return asyncRankingResults;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Set<? extends SearchResult> getUnsortedLoadedResults(String loaderKey) {
|
||||
return mResultsMap.containsKey(loaderKey) ?
|
||||
mResultsMap.get(loaderKey) : new HashSet<SearchResult>();
|
||||
return mResultsMap.containsKey(loaderKey) ? mResultsMap.get(loaderKey) : new HashSet<>();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
@@ -30,7 +30,6 @@ import com.android.settings.password.ChooseLockPattern.IntentBuilder;
|
||||
import com.android.settings.password.SetupChooseLockPattern;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
|
||||
@@ -49,7 +48,6 @@ import org.robolectric.res.builder.RobolectricPackageManager.ComponentState;
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowResources.SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class,
|
||||
ShadowEventLogWriter.class,
|
||||
ShadowUtils.class
|
||||
})
|
||||
|
@@ -16,6 +16,17 @@
|
||||
|
||||
package com.android.settings.applications;
|
||||
|
||||
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.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
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.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
@@ -35,7 +46,6 @@ import com.android.settings.TestConfig;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources.SettingsShadowTheme;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
|
||||
import com.android.settings.widget.LoadingViewController;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
@@ -53,17 +63,6 @@ import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
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.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests for {@link ManageApplications}.
|
||||
*/
|
||||
@@ -74,7 +73,6 @@ import static org.mockito.Mockito.when;
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class,
|
||||
ShadowEventLogWriter.class
|
||||
})
|
||||
public class ManageApplicationsTest {
|
||||
|
@@ -15,6 +15,19 @@
|
||||
*/
|
||||
package com.android.settings.dashboard;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -38,7 +51,6 @@ import com.android.settings.dashboard.suggestions.SuggestionAdapter;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settingslib.drawer.DashboardCategory;
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
|
||||
@@ -56,26 +68,12 @@ import org.robolectric.annotation.Config;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
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,
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowResources.SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class
|
||||
})
|
||||
public class DashboardAdapterTest {
|
||||
|
||||
|
@@ -36,7 +36,6 @@ import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.IFingerprintManager;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
|
||||
@@ -61,7 +60,6 @@ import org.robolectric.shadows.ShadowActivity.IntentForResult;
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowResources.SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class,
|
||||
ShadowEventLogWriter.class,
|
||||
ShadowUtils.class
|
||||
})
|
||||
|
@@ -30,7 +30,6 @@ import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.IFingerprintManager;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
|
||||
@@ -53,7 +52,6 @@ import org.robolectric.shadows.ShadowAlertDialog;
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowResources.SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class,
|
||||
ShadowEventLogWriter.class,
|
||||
ShadowUtils.class
|
||||
})
|
||||
|
@@ -17,6 +17,15 @@
|
||||
|
||||
package com.android.settings.fuelgauge;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -27,11 +36,10 @@ import android.support.v7.widget.RecyclerView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.applications.LayoutPreference;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
|
||||
import com.android.settings.widget.EntityHeaderController;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
@@ -45,23 +53,12 @@ import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
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,
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowResources.SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class,
|
||||
ShadowEntityHeaderController.class
|
||||
})
|
||||
public class BatteryHeaderPreferenceControllerTest {
|
||||
|
@@ -16,15 +16,17 @@
|
||||
package com.android.settings.fuelgauge;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.ColorFilter;
|
||||
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources.SettingsShadowTheme;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -34,10 +36,6 @@ import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
// TODO: Consider making the shadow class set global using a robolectric.properties file.
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH,
|
||||
@@ -45,7 +43,6 @@ import static org.mockito.Mockito.verify;
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class
|
||||
})
|
||||
public class BatteryMeterViewTest {
|
||||
private static final int BATTERY_LEVEL = 100;
|
||||
|
@@ -18,9 +18,7 @@ package com.android.settings.fuelgauge;
|
||||
import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_ADDITIONAL_BATTERY_INFO;
|
||||
import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_HIGH_POWER_APPS;
|
||||
import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_TOGGLE_APPS;
|
||||
|
||||
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.anyLong;
|
||||
@@ -34,8 +32,6 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.app.LoaderManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -68,7 +64,6 @@ import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.XmlTestUtils;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -95,7 +90,6 @@ import java.util.List;
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowResources.SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class
|
||||
})
|
||||
public class PowerUsageSummaryTest {
|
||||
private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
|
||||
|
@@ -31,7 +31,6 @@ import com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragme
|
||||
import com.android.settings.password.ChooseLockPassword.IntentBuilder;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
import com.android.setupwizardlib.GlifLayout;
|
||||
@@ -52,7 +51,6 @@ import org.robolectric.shadows.ShadowDrawable;
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowResources.SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class,
|
||||
ShadowEventLogWriter.class,
|
||||
ShadowUtils.class
|
||||
})
|
||||
|
@@ -29,7 +29,6 @@ import com.android.settings.password.ChooseLockPattern.ChooseLockPatternFragment
|
||||
import com.android.settings.password.ChooseLockPattern.IntentBuilder;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
import com.android.setupwizardlib.GlifLayout;
|
||||
@@ -48,7 +47,6 @@ import org.robolectric.shadows.ShadowDrawable;
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowResources.SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class,
|
||||
ShadowEventLogWriter.class,
|
||||
ShadowUtils.class
|
||||
})
|
||||
|
@@ -34,7 +34,6 @@ import com.android.settings.password.ChooseLockPassword.IntentBuilder;
|
||||
import com.android.settings.password.SetupChooseLockPassword.SetupChooseLockPasswordFragment;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
|
||||
@@ -62,7 +61,6 @@ import java.util.List;
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowResources.SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class,
|
||||
ShadowEventLogWriter.class,
|
||||
ShadowUtils.class
|
||||
})
|
||||
|
@@ -27,7 +27,6 @@ import com.android.settings.R;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
|
||||
@@ -40,13 +39,11 @@ import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowAlertDialog;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(
|
||||
manifest = TestConfig.MANIFEST_PATH,
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH,
|
||||
sdk = TestConfig.SDK_VERSION,
|
||||
shadows = {
|
||||
SettingsShadowResources.class,
|
||||
SettingsShadowResources.SettingsShadowTheme.class,
|
||||
ShadowDynamicIndexableContentMonitor.class,
|
||||
ShadowEventLogWriter.class,
|
||||
ShadowUtils.class
|
||||
})
|
||||
|
@@ -1,563 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.settings.search;
|
||||
|
||||
import 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;
|
||||
import android.app.LoaderManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.Loader;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.print.PrintManager;
|
||||
import android.print.PrintServicesLoader;
|
||||
import android.printservice.PrintServiceInfo;
|
||||
import android.provider.Settings;
|
||||
import android.provider.UserDictionary;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.settings.TestConfig;
|
||||
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;
|
||||
import com.android.settings.testutils.shadow.ShadowInputMethodManagerWithMethodList;
|
||||
import com.android.settings.testutils.shadow.ShadowPackageMonitor;
|
||||
|
||||
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.Robolectric;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.internal.ShadowExtractor;
|
||||
import org.robolectric.res.builder.RobolectricPackageManager;
|
||||
import org.robolectric.shadows.ShadowAccessibilityManager;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
import org.robolectric.shadows.ShadowContentResolver;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(
|
||||
manifest = TestConfig.MANIFEST_PATH,
|
||||
sdk = TestConfig.SDK_VERSION,
|
||||
shadows = {
|
||||
ShadowActivityWithLoadManager.class,
|
||||
ShadowContextImplWithRegisterReceiver.class,
|
||||
ShadowInputManager.class,
|
||||
ShadowInputMethodManagerWithMethodList.class,
|
||||
ShadowPackageMonitor.class,
|
||||
}
|
||||
)
|
||||
public class DynamicIndexableContentMonitorTest {
|
||||
|
||||
private static final int LOADER_ID = 1234;
|
||||
private static final String A11Y_PACKAGE_1 = "a11y-1";
|
||||
private static final String A11Y_PACKAGE_2 = "a11y-2";
|
||||
private static final String IME_PACKAGE_1 = "ime-1";
|
||||
private static final String IME_PACKAGE_2 = "ime-2";
|
||||
|
||||
@Mock
|
||||
private LoaderManager mLoaderManager;
|
||||
@Mock
|
||||
private DatabaseIndexingManager mIndexManager;
|
||||
|
||||
private Activity mActivity;
|
||||
private InputManager mInputManager;
|
||||
|
||||
private ShadowContextImplWithRegisterReceiver mShadowContextImpl;
|
||||
private ShadowActivityWithLoadManager mShadowActivity;
|
||||
private ShadowAccessibilityManager mShadowAccessibilityManager;
|
||||
private ShadowInputMethodManagerWithMethodList mShadowInputMethodManager;
|
||||
private RobolectricPackageManager mRobolectricPackageManager;
|
||||
|
||||
private final DynamicIndexableContentMonitor mMonitor = new DynamicIndexableContentMonitor();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mActivity = Robolectric.buildActivity(Activity.class).get();
|
||||
mInputManager = InputManager.getInstance();
|
||||
|
||||
// Robolectric shadows.
|
||||
mShadowContextImpl = (ShadowContextImplWithRegisterReceiver) ShadowExtractor.extract(
|
||||
((Application) ShadowApplication.getInstance().getApplicationContext())
|
||||
.getBaseContext());
|
||||
mShadowActivity = (ShadowActivityWithLoadManager) ShadowExtractor.extract(mActivity);
|
||||
mShadowAccessibilityManager = (ShadowAccessibilityManager) ShadowExtractor.extract(
|
||||
mActivity.getSystemService(Context.ACCESSIBILITY_SERVICE));
|
||||
mShadowInputMethodManager = (ShadowInputMethodManagerWithMethodList) ShadowExtractor
|
||||
.extract(mActivity.getSystemService(Context.INPUT_METHOD_SERVICE));
|
||||
mRobolectricPackageManager = RuntimeEnvironment.getRobolectricPackageManager();
|
||||
|
||||
// Setup shadows.
|
||||
mShadowContextImpl.setSystemService(Context.PRINT_SERVICE, mock(PrintManager.class));
|
||||
mShadowContextImpl.setSystemService(Context.INPUT_SERVICE, mInputManager);
|
||||
mShadowActivity.setLoaderManager(mLoaderManager);
|
||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(Collections.emptyList());
|
||||
mShadowInputMethodManager.setInputMethodList(Collections.emptyList());
|
||||
mRobolectricPackageManager.setSystemFeature(PackageManager.FEATURE_PRINTING, true);
|
||||
mRobolectricPackageManager.setSystemFeature(PackageManager.FEATURE_INPUT_METHODS, true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void shutDown() {
|
||||
mMonitor.unregister(mActivity, LOADER_ID);
|
||||
// BroadcastReceiver must be unregistered.
|
||||
assertThat(extractPackageMonitor()).isNull();
|
||||
|
||||
DynamicIndexableContentMonitor.resetForTesting();
|
||||
mRobolectricPackageManager.reset();
|
||||
|
||||
DatabaseTestUtils.clearDb(mActivity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLockedUser() {
|
||||
mMonitor.register(mActivity, LOADER_ID, mIndexManager, false /* isUserUnlocked */);
|
||||
|
||||
// No loader procedure happens.
|
||||
verify(mLoaderManager, never()).initLoader(
|
||||
anyInt(), any(Bundle.class), any(LoaderManager.LoaderCallbacks.class));
|
||||
// No indexing happens.
|
||||
verify(mIndexManager, never()).updateFromClassNameResource(
|
||||
anyString(), anyBoolean());
|
||||
|
||||
mMonitor.unregister(mActivity, LOADER_ID);
|
||||
|
||||
// No destroy loader should happen.
|
||||
verify(mLoaderManager, never()).destroyLoader(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNoPrintingFeature() {
|
||||
mRobolectricPackageManager.setSystemFeature(PackageManager.FEATURE_PRINTING, false);
|
||||
|
||||
mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
|
||||
|
||||
// No loader procedure happens.
|
||||
verify(mLoaderManager, never()).initLoader(
|
||||
anyInt(), any(Bundle.class), any(LoaderManager.LoaderCallbacks.class));
|
||||
verifyNoIndexing(PrintSettingsFragment.class);
|
||||
|
||||
mMonitor.unregister(mActivity, LOADER_ID);
|
||||
|
||||
// No destroy loader should happen.
|
||||
verify(mLoaderManager, never()).destroyLoader(anyInt());
|
||||
// BroadcastReceiver must be unregistered.
|
||||
assertThat(extractPackageMonitor()).isNull();
|
||||
|
||||
// To suppress spurious test fail in {@link #shutDown()}.
|
||||
mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrinterServiceIndex() {
|
||||
mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
|
||||
|
||||
// Loader procedure happens.
|
||||
verify(mLoaderManager, only()).initLoader(LOADER_ID, null, mMonitor);
|
||||
|
||||
// Loading print services happens.
|
||||
final Loader<List<PrintServiceInfo>> loader =
|
||||
mMonitor.onCreateLoader(LOADER_ID, null /* args */);
|
||||
assertThat(loader).isInstanceOf(PrintServicesLoader.class);
|
||||
verifyNoIndexing(PrintSettingsFragment.class);
|
||||
|
||||
mMonitor.onLoadFinished(loader, Collections.emptyList());
|
||||
|
||||
verifyIncrementalIndexing(PrintSettingsFragment.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInputDevicesMonitor() {
|
||||
mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
|
||||
|
||||
// Rebuild indexing should happen.
|
||||
verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
|
||||
// Input monitor should be registered to InputManager.
|
||||
final InputManager.InputDeviceListener listener = extactInputDeviceListener();
|
||||
assertThat(listener).isNotNull();
|
||||
|
||||
/*
|
||||
* Nothing happens on successive register calls.
|
||||
*/
|
||||
mMonitor.unregister(mActivity, LOADER_ID);
|
||||
reset(mIndexManager);
|
||||
|
||||
mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
|
||||
|
||||
verifyNoIndexing(PhysicalKeyboardFragment.class);
|
||||
assertThat(extactInputDeviceListener()).isEqualTo(listener);
|
||||
|
||||
/*
|
||||
* A device is added.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
listener.onInputDeviceAdded(1 /* deviceId */);
|
||||
|
||||
verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
|
||||
|
||||
/*
|
||||
* A device is removed.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
listener.onInputDeviceRemoved(2 /* deviceId */);
|
||||
|
||||
verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
|
||||
|
||||
/*
|
||||
* A device is changed.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
listener.onInputDeviceChanged(3 /* deviceId */);
|
||||
|
||||
verifyIncrementalIndexing(PhysicalKeyboardFragment.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInputMethodServicesMonitor() throws Exception {
|
||||
mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
|
||||
|
||||
verifyIncrementalIndexing(VirtualKeyboardFragment.class);
|
||||
verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
|
||||
|
||||
final Uri enabledInputMethodsContentUri = Settings.Secure.getUriFor(
|
||||
Settings.Secure.ENABLED_INPUT_METHODS);
|
||||
// Content observer should be registered.
|
||||
final ContentObserver observer = extractContentObserver(enabledInputMethodsContentUri);
|
||||
assertThat(observer).isNotNull();
|
||||
|
||||
/*
|
||||
* When an input method service package is installed, incremental indexing happen.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
installInputMethodService(IME_PACKAGE_1);
|
||||
|
||||
verifyIncrementalIndexing(VirtualKeyboardFragment.class);
|
||||
verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
|
||||
|
||||
/*
|
||||
* When another input method service package is installed, incremental indexing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
installInputMethodService(IME_PACKAGE_2);
|
||||
|
||||
verifyIncrementalIndexing(VirtualKeyboardFragment.class);
|
||||
verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
|
||||
|
||||
/*
|
||||
* When an input method service is disabled, rebuild indexing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
disableInstalledPackage(IME_PACKAGE_1);
|
||||
|
||||
verifyIncrementalIndexing(VirtualKeyboardFragment.class);
|
||||
verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
|
||||
|
||||
/*
|
||||
* When an input method service is enabled, incremental indexing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
enableInstalledPackage(IME_PACKAGE_1);
|
||||
|
||||
verifyIncrementalIndexing(VirtualKeyboardFragment.class);
|
||||
verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
|
||||
|
||||
/*
|
||||
* When an input method service package is uninstalled, rebuild indexing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
uninstallInputMethodService(IME_PACKAGE_1);
|
||||
|
||||
verifyIncrementalIndexing(VirtualKeyboardFragment.class);
|
||||
verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
|
||||
|
||||
/*
|
||||
* When an accessibility service package is installed, nothing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
installAccessibilityService(A11Y_PACKAGE_1);
|
||||
|
||||
verifyNoIndexing(VirtualKeyboardFragment.class);
|
||||
verifyNoIndexing(AvailableVirtualKeyboardFragment.class);
|
||||
|
||||
/*
|
||||
* When enabled IMEs list is changed, rebuild indexing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
observer.onChange(false /* selfChange */, enabledInputMethodsContentUri);
|
||||
|
||||
verifyIncrementalIndexing(VirtualKeyboardFragment.class);
|
||||
verifyIncrementalIndexing(AvailableVirtualKeyboardFragment.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserDictionaryChangeMonitor() throws Exception {
|
||||
mMonitor.register(mActivity, LOADER_ID, mIndexManager, true /* isUserUnlocked */);
|
||||
|
||||
// Content observer should be registered.
|
||||
final ContentObserver observer = extractContentObserver(UserDictionary.Words.CONTENT_URI);
|
||||
assertThat(observer).isNotNull();
|
||||
|
||||
verifyIncrementalIndexing(LanguageAndInputSettings.class);
|
||||
|
||||
/*
|
||||
* When user dictionary content is changed, rebuild indexing happens.
|
||||
*/
|
||||
reset(mIndexManager);
|
||||
|
||||
observer.onChange(false /* selfChange */, UserDictionary.Words.CONTENT_URI);
|
||||
|
||||
verifyIncrementalIndexing(LanguageAndInputSettings.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verification helpers.
|
||||
*/
|
||||
|
||||
private void verifyNoIndexing(Class<?> indexingClass) {
|
||||
verify(mIndexManager, never()).updateFromClassNameResource(eq(indexingClass.getName()),
|
||||
anyBoolean());
|
||||
}
|
||||
|
||||
private void verifyIncrementalIndexing(Class<?> indexingClass) {
|
||||
verify(mIndexManager, times(1)).updateFromClassNameResource(indexingClass.getName(),
|
||||
true /* includeInSearchResults */);
|
||||
verify(mIndexManager, never()).updateFromClassNameResource(indexingClass.getName(),
|
||||
false /* includeInSearchResults */);
|
||||
}
|
||||
|
||||
/*
|
||||
* Testing helper methods.
|
||||
*/
|
||||
|
||||
private InputManager.InputDeviceListener extactInputDeviceListener() {
|
||||
List<InputManager.InputDeviceListener> listeners = ((ShadowInputManager) ShadowExtractor
|
||||
.extract(mInputManager))
|
||||
.getRegisteredInputDeviceListeners();
|
||||
InputManager.InputDeviceListener inputDeviceListener = null;
|
||||
for (InputManager.InputDeviceListener listener : listeners) {
|
||||
if (isUnderTest(listener)) {
|
||||
if (inputDeviceListener != null) {
|
||||
assertThat(listener).isEqualTo(inputDeviceListener);
|
||||
} else {
|
||||
inputDeviceListener = listener;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputDeviceListener;
|
||||
}
|
||||
|
||||
private PackageMonitor extractPackageMonitor() {
|
||||
List<ShadowApplication.Wrapper> receivers = ShadowApplication.getInstance()
|
||||
.getRegisteredReceivers();
|
||||
PackageMonitor packageMonitor = null;
|
||||
for (ShadowApplication.Wrapper wrapper : receivers) {
|
||||
BroadcastReceiver receiver = wrapper.getBroadcastReceiver();
|
||||
if (isUnderTest(receiver) && receiver instanceof PackageMonitor) {
|
||||
if (packageMonitor != null) {
|
||||
assertThat(receiver).isEqualTo(packageMonitor);
|
||||
} else {
|
||||
packageMonitor = (PackageMonitor) receiver;
|
||||
}
|
||||
}
|
||||
}
|
||||
return packageMonitor;
|
||||
}
|
||||
|
||||
private ContentObserver extractContentObserver(Uri uri) {
|
||||
ShadowContentResolver contentResolver = (ShadowContentResolver) ShadowExtractor
|
||||
.extract(mActivity.getContentResolver());
|
||||
Collection<ContentObserver> observers = contentResolver.getContentObservers(uri);
|
||||
ContentObserver contentObserver = null;
|
||||
for (ContentObserver observer : observers) {
|
||||
if (isUnderTest(observer)) {
|
||||
if (contentObserver != null) {
|
||||
assertThat(observer).isEqualTo(contentObserver);
|
||||
} else {
|
||||
contentObserver = observer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return contentObserver;
|
||||
}
|
||||
|
||||
private void enableInstalledPackage(String packageName) {
|
||||
((PackageManager) mRobolectricPackageManager).setApplicationEnabledSetting(
|
||||
packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0 /* flags */);
|
||||
extractPackageMonitor().onPackageModified(packageName);
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
}
|
||||
|
||||
private void disableInstalledPackage(String packageName) {
|
||||
((PackageManager) mRobolectricPackageManager).setApplicationEnabledSetting(
|
||||
packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0 /* flags */);
|
||||
extractPackageMonitor().onPackageModified(packageName);
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
}
|
||||
|
||||
private void installAccessibilityService(String packageName) throws Exception {
|
||||
final AccessibilityServiceInfo serviceToAdd = buildAccessibilityServiceInfo(packageName);
|
||||
|
||||
final List<AccessibilityServiceInfo> services = new ArrayList<>();
|
||||
services.addAll(mShadowAccessibilityManager.getInstalledAccessibilityServiceList());
|
||||
services.add(serviceToAdd);
|
||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(services);
|
||||
|
||||
final Intent intent = DynamicIndexableContentMonitor
|
||||
.getAccessibilityServiceIntent(packageName);
|
||||
mRobolectricPackageManager.addResolveInfoForIntent(intent, serviceToAdd.getResolveInfo());
|
||||
mRobolectricPackageManager.addPackage(packageName);
|
||||
|
||||
extractPackageMonitor()
|
||||
.onPackageAppeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
}
|
||||
|
||||
private void uninstallAccessibilityService(String packageName) throws Exception {
|
||||
final AccessibilityServiceInfo serviceToRemove = buildAccessibilityServiceInfo(packageName);
|
||||
|
||||
final List<AccessibilityServiceInfo> services = new ArrayList<>();
|
||||
services.addAll(mShadowAccessibilityManager.getInstalledAccessibilityServiceList());
|
||||
services.remove(serviceToRemove);
|
||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(services);
|
||||
|
||||
final Intent intent = DynamicIndexableContentMonitor
|
||||
.getAccessibilityServiceIntent(packageName);
|
||||
mRobolectricPackageManager.removeResolveInfosForIntent(intent, packageName);
|
||||
mRobolectricPackageManager.removePackage(packageName);
|
||||
|
||||
extractPackageMonitor()
|
||||
.onPackageDisappeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
}
|
||||
|
||||
private void installInputMethodService(String packageName) throws Exception {
|
||||
final ResolveInfo resolveInfoToAdd = buildResolveInfo(packageName, "imeService");
|
||||
final InputMethodInfo serviceToAdd = buildInputMethodInfo(resolveInfoToAdd);
|
||||
|
||||
final List<InputMethodInfo> services = new ArrayList<>();
|
||||
services.addAll(mShadowInputMethodManager.getInputMethodList());
|
||||
services.add(serviceToAdd);
|
||||
mShadowInputMethodManager.setInputMethodList(services);
|
||||
|
||||
final Intent intent = DynamicIndexableContentMonitor.getIMEServiceIntent(packageName);
|
||||
mRobolectricPackageManager.addResolveInfoForIntent(intent, resolveInfoToAdd);
|
||||
mRobolectricPackageManager.addPackage(packageName);
|
||||
|
||||
extractPackageMonitor()
|
||||
.onPackageAppeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
}
|
||||
|
||||
private void uninstallInputMethodService(String packageName) throws Exception {
|
||||
final ResolveInfo resolveInfoToRemove = buildResolveInfo(packageName, "imeService");
|
||||
final InputMethodInfo serviceToRemove = buildInputMethodInfo(resolveInfoToRemove);
|
||||
|
||||
final List<InputMethodInfo> services = new ArrayList<>();
|
||||
services.addAll(mShadowInputMethodManager.getInputMethodList());
|
||||
services.remove(serviceToRemove);
|
||||
mShadowInputMethodManager.setInputMethodList(services);
|
||||
|
||||
final Intent intent = DynamicIndexableContentMonitor.getIMEServiceIntent(packageName);
|
||||
mRobolectricPackageManager.removeResolveInfosForIntent(intent, packageName);
|
||||
mRobolectricPackageManager.removePackage(packageName);
|
||||
|
||||
extractPackageMonitor()
|
||||
.onPackageDisappeared(packageName, PackageMonitor.PACKAGE_PERMANENT_CHANGE);
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
}
|
||||
|
||||
private AccessibilityServiceInfo buildAccessibilityServiceInfo(String packageName)
|
||||
throws IOException, XmlPullParserException {
|
||||
return new AccessibilityServiceInfo(
|
||||
buildResolveInfo(packageName, "A11yService"), mActivity);
|
||||
}
|
||||
|
||||
private static InputMethodInfo buildInputMethodInfo(ResolveInfo resolveInfo) {
|
||||
return new InputMethodInfo(resolveInfo, false /* isAuxIme */, "SettingsActivity",
|
||||
null /* subtypes */, 0 /* defaultResId */, false /* forceDefault */);
|
||||
}
|
||||
|
||||
private static ResolveInfo buildResolveInfo(String packageName, String className) {
|
||||
final ResolveInfo resolveInfo = new ResolveInfo();
|
||||
resolveInfo.serviceInfo = new ServiceInfo();
|
||||
resolveInfo.serviceInfo.packageName = packageName;
|
||||
resolveInfo.serviceInfo.name = className;
|
||||
// To workaround that RobolectricPackageManager.removeResolveInfosForIntent() only works
|
||||
// for activity/broadcast resolver.
|
||||
resolveInfo.activityInfo = new ActivityInfo();
|
||||
resolveInfo.activityInfo.packageName = packageName;
|
||||
resolveInfo.activityInfo.name = className;
|
||||
|
||||
return resolveInfo;
|
||||
}
|
||||
|
||||
private static boolean isUnderTest(Object object) {
|
||||
return object.getClass().getName().startsWith(
|
||||
DynamicIndexableContentMonitor.class.getName());
|
||||
}
|
||||
}
|
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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 android.content.Context.INPUT_METHOD_SERVICE;
|
||||
import static com.android.settings.search.InputDeviceResultLoader.PHYSICAL_KEYBOARD_FRAGMENT;
|
||||
import static com.android.settings.search.InputDeviceResultLoader.VIRTUAL_KEYBOARD_FRAGMENT;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.view.InputDevice;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.dashboard.SiteMapManager;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.ShadowInputDevice;
|
||||
|
||||
import org.junit.After;
|
||||
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.RuntimeEnvironment;
|
||||
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,
|
||||
shadows = {
|
||||
ShadowInputDevice.class
|
||||
})
|
||||
public class InputDeviceResultLoaderTest {
|
||||
|
||||
private static final String QUERY = "test_query";
|
||||
private static final List<String> PHYSICAL_KEYBOARD_BREADCRUMB;
|
||||
private static final List<String> VIRTUAL_KEYBOARD_BREADCRUMB;
|
||||
|
||||
static {
|
||||
PHYSICAL_KEYBOARD_BREADCRUMB = new ArrayList<>();
|
||||
VIRTUAL_KEYBOARD_BREADCRUMB = new ArrayList<>();
|
||||
PHYSICAL_KEYBOARD_BREADCRUMB.add("Settings");
|
||||
PHYSICAL_KEYBOARD_BREADCRUMB.add("physical keyboard");
|
||||
VIRTUAL_KEYBOARD_BREADCRUMB.add("Settings");
|
||||
VIRTUAL_KEYBOARD_BREADCRUMB.add("virtual keyboard");
|
||||
}
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private SiteMapManager mSiteMapManager;
|
||||
@Mock
|
||||
private InputManager mInputManager;
|
||||
@Mock
|
||||
private InputMethodManager mImm;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private InputDeviceResultLoader mLoader;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mContext.getApplicationContext()).thenReturn(mContext);
|
||||
when(mContext.getSystemService(Context.INPUT_SERVICE))
|
||||
.thenReturn(mInputManager);
|
||||
when(mContext.getSystemService(INPUT_METHOD_SERVICE))
|
||||
.thenReturn(mImm);
|
||||
when(mContext.getPackageManager())
|
||||
.thenReturn(mPackageManager);
|
||||
when(mContext.getString(anyInt()))
|
||||
.thenAnswer(invocation -> RuntimeEnvironment.application.getString(
|
||||
(Integer) invocation.getArguments()[0]));
|
||||
mLoader = new InputDeviceResultLoader(mContext, QUERY, mSiteMapManager);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowInputDevice.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void query_noKeyboard_shouldNotReturnAnything() {
|
||||
assertThat(mLoader.loadInBackground()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void query_hasPhysicalKeyboard_match() {
|
||||
addPhysicalKeyboard(QUERY);
|
||||
when(mSiteMapManager.buildBreadCrumb(mContext, PHYSICAL_KEYBOARD_FRAGMENT,
|
||||
RuntimeEnvironment.application.getString(R.string.physical_keyboard_title)))
|
||||
.thenReturn(PHYSICAL_KEYBOARD_BREADCRUMB);
|
||||
|
||||
final List<SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
|
||||
|
||||
assertThat(results).hasSize(1);
|
||||
assertThat(results.get(0).title).isEqualTo(QUERY);
|
||||
assertThat(results.get(0).breadcrumbs)
|
||||
.containsExactlyElementsIn(PHYSICAL_KEYBOARD_BREADCRUMB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void query_hasVirtualKeyboard_match() {
|
||||
addVirtualKeyboard(QUERY);
|
||||
when(mSiteMapManager.buildBreadCrumb(mContext, VIRTUAL_KEYBOARD_FRAGMENT,
|
||||
RuntimeEnvironment.application.getString(R.string.add_virtual_keyboard)))
|
||||
.thenReturn(VIRTUAL_KEYBOARD_BREADCRUMB);
|
||||
|
||||
final List<SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
|
||||
assertThat(results).hasSize(1);
|
||||
assertThat(results.get(0).title).isEqualTo(QUERY);
|
||||
assertThat(results.get(0).breadcrumbs)
|
||||
.containsExactlyElementsIn(VIRTUAL_KEYBOARD_BREADCRUMB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void query_hasPhysicalVirtualKeyboard_doNotMatch() {
|
||||
addPhysicalKeyboard("abc");
|
||||
addVirtualKeyboard("def");
|
||||
|
||||
assertThat(mLoader.loadInBackground()).isEmpty();
|
||||
verifyZeroInteractions(mSiteMapManager);
|
||||
}
|
||||
|
||||
private void addPhysicalKeyboard(String name) {
|
||||
final InputDevice device = mock(InputDevice.class);
|
||||
when(device.isVirtual()).thenReturn(false);
|
||||
when(device.isFullKeyboard()).thenReturn(true);
|
||||
when(device.getName()).thenReturn(name);
|
||||
ShadowInputDevice.sDeviceIds = new int[]{0};
|
||||
ShadowInputDevice.addDevice(0, device);
|
||||
}
|
||||
|
||||
private void addVirtualKeyboard(String name) {
|
||||
final List<InputMethodInfo> imis = new ArrayList<>();
|
||||
final InputMethodInfo info = mock(InputMethodInfo.class);
|
||||
imis.add(info);
|
||||
when(info.getServiceInfo()).thenReturn(new ServiceInfo());
|
||||
when(info.loadLabel(mPackageManager)).thenReturn(name);
|
||||
info.getServiceInfo().packageName = "pkg";
|
||||
info.getServiceInfo().name = "class";
|
||||
when(mImm.getInputMethodList()).thenReturn(imis);
|
||||
}
|
||||
|
||||
}
|
@@ -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 MockAccessibilityLoader extends AccessibilityServiceResultLoader {
|
||||
|
||||
public MockAccessibilityLoader(Context context) {
|
||||
super(context, "test_query", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends SearchResult> loadInBackground() {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDiscardResult(Set<? extends SearchResult> result) {
|
||||
|
||||
}
|
||||
}
|
@@ -21,9 +21,8 @@ import android.content.Context;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class MockAccessiblityLoader extends AccessibilityServiceResultLoader {
|
||||
|
||||
public MockAccessiblityLoader(Context context) {
|
||||
public class MockInputDeviceResultLoader extends InputDeviceResultLoader {
|
||||
public MockInputDeviceResultLoader(Context context) {
|
||||
super(context, "test_query", null);
|
||||
}
|
||||
|
@@ -17,6 +17,20 @@
|
||||
|
||||
package com.android.settings.search;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
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.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.LoaderManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -28,10 +42,10 @@ import android.view.View;
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.testutils.DatabaseTestUtils;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -52,20 +66,6 @@ import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
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.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,
|
||||
@@ -83,6 +83,8 @@ public class SearchFragmentTest {
|
||||
private InstalledAppResultLoader mInstalledAppResultLoader;
|
||||
@Mock
|
||||
private AccessibilityServiceResultLoader mAccessibilityServiceResultLoader;
|
||||
@Mock
|
||||
private InputDeviceResultLoader mInputDeviceResultLoader;
|
||||
|
||||
@Mock
|
||||
private SavedQueryLoader mSavedQueryLoader;
|
||||
@@ -118,6 +120,9 @@ public class SearchFragmentTest {
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getAccessibilityServiceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mAccessibilityServiceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getInputDeviceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mInputDeviceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
|
||||
@@ -178,6 +183,9 @@ public class SearchFragmentTest {
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getAccessibilityServiceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mAccessibilityServiceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getInputDeviceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mInputDeviceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
|
||||
@@ -236,6 +244,9 @@ public class SearchFragmentTest {
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getAccessibilityServiceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mAccessibilityServiceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getInputDeviceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mInputDeviceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
ActivityController<SearchActivity> activityController =
|
||||
@@ -270,6 +281,9 @@ public class SearchFragmentTest {
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getAccessibilityServiceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mAccessibilityServiceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getInputDeviceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(mInputDeviceResultLoader);
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
|
||||
@@ -349,7 +363,10 @@ public class SearchFragmentTest {
|
||||
.thenReturn(new MockAppLoader(RuntimeEnvironment.application));
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getAccessibilityServiceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(new MockAccessiblityLoader(RuntimeEnvironment.application));
|
||||
.thenReturn(new MockAccessibilityLoader(RuntimeEnvironment.application));
|
||||
when(mFeatureFactory.searchFeatureProvider
|
||||
.getInputDeviceResultLoader(any(Context.class), anyString()))
|
||||
.thenReturn(new MockInputDeviceResultLoader(RuntimeEnvironment.application));
|
||||
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
|
||||
.thenReturn(mSavedQueryLoader);
|
||||
ActivityController<SearchActivity> activityController =
|
||||
|
@@ -1,22 +0,0 @@
|
||||
package com.android.settings.testutils.shadow;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.UserManager;
|
||||
import com.android.settings.search.DynamicIndexableContentMonitor;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.annotation.RealObject;
|
||||
|
||||
/**
|
||||
* A shadow class of {@link DynamicIndexableContentMonitor}. The real implementation of
|
||||
* {@link DynamicIndexableContentMonitor#register} calls {@link UserManager#isUserUnlocked()}, which
|
||||
* Robolectric has not yet been updated to support, so throws a NoSuchMethodError exception.
|
||||
*/
|
||||
// TODO: Delete this once Robolectric is updated to the latest SDK.
|
||||
@Implements(DynamicIndexableContentMonitor.class)
|
||||
public class ShadowDynamicIndexableContentMonitor {
|
||||
|
||||
@Implementation
|
||||
public void register(Activity activity, int loaderId) {
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user