Refactor DynamicIndexableContentMonitor
Refactor content monitoring code into a few singletons to keep alive
while Settings app is running.
Bug: 32995210
Test: Manually installing/uninstalling AOSP LatinIME.apk while
Settings app is/isn't running, then search AOSP.
Test: Connecting/Disconnecting Anker bluetooth keyboard while Settings
app is/isn't running, then search Anker.
Test: Added Robolectric test for DynamicIndexableContentMonitor.
Change-Id: I588e33be169fc9677d41c3daa59ab400f04f6419
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index cc203f7..b9be118 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -723,7 +723,7 @@
unregisterReceiver(mBatteryInfoReceiver);
unregisterReceiver(mUserAddRemoveReceiver);
if (mDynamicIndexableContentMonitor != null) {
- mDynamicIndexableContentMonitor.unregister();
+ mDynamicIndexableContentMonitor.unregister(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
}
}
diff --git a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
index 44bf435..e122244 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
@@ -222,6 +222,7 @@
InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(
Context.INPUT_METHOD_SERVICE);
+ // TODO: Move to VirtualKeyboardFragment and AvailableVirtualKeyboardFragment.
// All other IMEs.
List<InputMethodInfo> inputMethods = immValues.getInputMethodList();
final int inputMethodCount = (inputMethods == null ? 0 : inputMethods.size());
@@ -245,6 +246,7 @@
indexables.add(indexable);
}
+ // TODO: Move to PhysicalKeyboardFragment.
// Hard keyboards
InputManager inputManager = (InputManager) context.getSystemService(
Context.INPUT_SERVICE);
diff --git a/src/com/android/settings/search/DynamicIndexableContentMonitor.java b/src/com/android/settings/search/DynamicIndexableContentMonitor.java
index a24ec50..34cdeba 100644
--- a/src/com/android/settings/search/DynamicIndexableContentMonitor.java
+++ b/src/com/android/settings/search/DynamicIndexableContentMonitor.java
@@ -20,6 +20,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Activity;
import android.app.LoaderManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
@@ -30,17 +31,17 @@
import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import android.os.UserHandle;
import android.os.UserManager;
import android.print.PrintManager;
import android.print.PrintServicesLoader;
import android.printservice.PrintServiceInfo;
import android.provider.UserDictionary;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.util.Log;
import android.view.accessibility.AccessibilityManager;
+import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
@@ -52,227 +53,100 @@
import java.util.ArrayList;
import java.util.List;
-public final class DynamicIndexableContentMonitor extends PackageMonitor implements
- InputManager.InputDeviceListener,
+public final class DynamicIndexableContentMonitor implements
LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
- private static final String TAG = "DynamicIndexableContentMonitor";
+ // 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;
+ @VisibleForTesting
+ static final long DELAY_PROCESS_PACKAGE_CHANGE = 2000;
- private static final int MSG_PACKAGE_AVAILABLE = 1;
- private static final int MSG_PACKAGE_UNAVAILABLE = 2;
-
- private final List<String> mAccessibilityServices = new ArrayList<String>();
- private final List<String> mImeServices = new ArrayList<String>();
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_PACKAGE_AVAILABLE: {
- String packageName = (String) msg.obj;
- handlePackageAvailable(packageName);
- } break;
-
- case MSG_PACKAGE_UNAVAILABLE: {
- String packageName = (String) msg.obj;
- handlePackageUnavailable(packageName);
- } break;
- }
- }
- };
-
- private final ContentObserver mUserDictionaryContentObserver =
- new UserDictionaryContentObserver(mHandler);
-
+ // Null if not initialized.
+ @Nullable private Index mIndex;
private Context mContext;
- private boolean mHasFeatureIme;
- private boolean mRegistered;
+ private boolean mHasFeaturePrinting;
- private static Intent getAccessibilityServiceIntent(String packageName) {
+ @VisibleForTesting
+ static Intent getAccessibilityServiceIntent(String packageName) {
final Intent intent = new Intent(AccessibilityService.SERVICE_INTERFACE);
intent.setPackage(packageName);
return intent;
}
- private static Intent getIMEServiceIntent(String packageName) {
- final Intent intent = new Intent("android.view.InputMethod");
+ @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();
+ PackageChangeMonitor.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) {
- mContext = activity;
+ final boolean isUserUnlocked = activity
+ .getSystemService(UserManager.class)
+ .isUserUnlocked();
+ register(activity, loaderId, Index.getInstance(activity), isUserUnlocked);
+ }
- if (!mContext.getSystemService(UserManager.class).isUserUnlocked()) {
+ /**
+ * For testing to inject {@link Index} 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, Index index, boolean isUserUnlocked) {
+ if (!isUserUnlocked) {
Log.w(TAG, "Skipping content monitoring because user is locked");
- mRegistered = false;
return;
- } else {
- mRegistered = true;
}
+ mContext = activity;
+ mIndex = index;
- boolean hasFeaturePrinting = mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_PRINTING);
- mHasFeatureIme = mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_INPUT_METHODS);
-
- // Cache accessibility service packages to know when they go away.
- AccessibilityManager accessibilityManager = (AccessibilityManager)
- mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- List<AccessibilityServiceInfo> accessibilityServices = accessibilityManager
- .getInstalledAccessibilityServiceList();
- final int accessibilityServiceCount = accessibilityServices.size();
- for (int i = 0; i < accessibilityServiceCount; i++) {
- AccessibilityServiceInfo accessibilityService = accessibilityServices.get(i);
- ResolveInfo resolveInfo = accessibilityService.getResolveInfo();
- if (resolveInfo == null || resolveInfo.serviceInfo == null) {
- continue;
- }
- mAccessibilityServices.add(resolveInfo.serviceInfo.packageName);
- }
-
- if (hasFeaturePrinting) {
- activity.getLoaderManager().initLoader(loaderId, null, this);
- }
-
- // Cache IME service packages to know when they go away.
- if (mHasFeatureIme) {
- InputMethodManager imeManager = (InputMethodManager)
- mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
- List<InputMethodInfo> inputMethods = imeManager.getInputMethodList();
- final int inputMethodCount = inputMethods.size();
- for (int i = 0; i < inputMethodCount; i++) {
- InputMethodInfo inputMethod = inputMethods.get(i);
- ServiceInfo serviceInfo = inputMethod.getServiceInfo();
- if (serviceInfo == null) continue;
- mImeServices.add(serviceInfo.packageName);
- }
-
- // Watch for related content URIs.
- mContext.getContentResolver().registerContentObserver(
- UserDictionary.Words.CONTENT_URI, true, mUserDictionaryContentObserver);
+ mHasFeaturePrinting = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_PRINTING);
+ if (mHasFeaturePrinting) {
+ activity.getLoaderManager().initLoader(loaderId, null /* args */, this /* callbacks */);
}
// Watch for input device changes.
- InputManager inputManager = (InputManager) activity.getSystemService(
- Context.INPUT_SERVICE);
- inputManager.registerInputDeviceListener(this, mHandler);
+ InputDevicesMonitor.getInstance().initialize(mContext, mIndex);
// Start tracking packages.
- register(activity, Looper.getMainLooper(), UserHandle.CURRENT, false);
+ PackageChangeMonitor.getInstance().initialize(mContext, mIndex);
}
- @Override
- public void unregister() {
- if (!mRegistered) return;
+ /**
+ * 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 (mIndex == null) return;
- super.unregister();
-
- InputManager inputManager = (InputManager) mContext.getSystemService(
- Context.INPUT_SERVICE);
- inputManager.unregisterInputDeviceListener(this);
-
- if (mHasFeatureIme) {
- mContext.getContentResolver().unregisterContentObserver(
- mUserDictionaryContentObserver);
- }
-
- mAccessibilityServices.clear();
- mImeServices.clear();
- }
-
- // Covers installed, appeared external storage with the package, upgraded.
- @Override
- public void onPackageAppeared(String packageName, int uid) {
- postMessage(MSG_PACKAGE_AVAILABLE, packageName);
- }
-
- // Covers uninstalled, removed external storage with the package.
- @Override
- public void onPackageDisappeared(String packageName, int uid) {
- postMessage(MSG_PACKAGE_UNAVAILABLE, packageName);
- }
-
- // Covers enabled, disabled.
- @Override
- public void onPackageModified(String packageName) {
- super.onPackageModified(packageName);
- try {
- final int state = mContext.getPackageManager().getApplicationEnabledSetting(
- packageName);
- if (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- || state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
- postMessage(MSG_PACKAGE_AVAILABLE, packageName);
- } else {
- postMessage(MSG_PACKAGE_UNAVAILABLE, packageName);
- }
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Package does not exist: " + packageName, e);
- }
- }
-
- @Override
- public void onInputDeviceAdded(int deviceId) {
- Index.getInstance(mContext).updateFromClassNameResource(
- InputMethodAndLanguageSettings.class.getName(), false, true);
- }
-
- @Override
- public void onInputDeviceRemoved(int deviceId) {
- onInputDeviceChanged(deviceId);
- }
-
- @Override
- public void onInputDeviceChanged(int deviceId) {
- Index.getInstance(mContext).updateFromClassNameResource(
- InputMethodAndLanguageSettings.class.getName(), true, true);
- }
-
- private void postMessage(int what, String packageName) {
- Message message = mHandler.obtainMessage(what, packageName);
- mHandler.sendMessageDelayed(message, DELAY_PROCESS_PACKAGE_CHANGE);
- }
-
- private void handlePackageAvailable(String packageName) {
- if (!mAccessibilityServices.contains(packageName)) {
- final Intent intent = getAccessibilityServiceIntent(packageName);
- List<?> services = mContext.getPackageManager().queryIntentServices(intent, 0);
- if (services != null && !services.isEmpty()) {
- mAccessibilityServices.add(packageName);
- Index.getInstance(mContext).updateFromClassNameResource(
- AccessibilitySettings.class.getName(), false, true);
- }
- }
-
- if (mHasFeatureIme) {
- if (!mImeServices.contains(packageName)) {
- Intent intent = getIMEServiceIntent(packageName);
- List<?> services = mContext.getPackageManager().queryIntentServices(intent, 0);
- if (services != null && !services.isEmpty()) {
- mImeServices.add(packageName);
- Index.getInstance(mContext).updateFromClassNameResource(
- InputMethodAndLanguageSettings.class.getName(), false, true);
- }
- }
- }
- }
-
- private void handlePackageUnavailable(String packageName) {
- final int accessibilityIndex = mAccessibilityServices.indexOf(packageName);
- if (accessibilityIndex >= 0) {
- mAccessibilityServices.remove(accessibilityIndex);
- Index.getInstance(mContext).updateFromClassNameResource(
- AccessibilitySettings.class.getName(), true, true);
- }
-
- if (mHasFeatureIme) {
- final int imeIndex = mImeServices.indexOf(packageName);
- if (imeIndex >= 0) {
- mImeServices.remove(imeIndex);
- Index.getInstance(mContext).updateFromClassNameResource(
- InputMethodAndLanguageSettings.class.getName(), true, true);
- }
+ if (mHasFeaturePrinting) {
+ activity.getLoaderManager().destroyLoader(loaderId);
}
}
@@ -286,8 +160,8 @@
@Override
public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
List<PrintServiceInfo> services) {
- Index.getInstance(mContext).updateFromClassNameResource(
- PrintSettingsFragment.class.getName(), false, true);
+ mIndex.updateFromClassNameResource(PrintSettingsFragment.class.getName(),
+ false /* rebuild */, true /* includeInSearchResult */);
}
@Override
@@ -295,18 +169,304 @@
// nothing to do
}
- private final class UserDictionaryContentObserver extends ContentObserver {
+ // A singleton that monitors input devices changes and updates indexes of physical keyboards.
+ private static class InputDevicesMonitor implements InputManager.InputDeviceListener {
- public UserDictionaryContentObserver(Handler handler) {
- super(handler);
+ // Null if not initialized.
+ @Nullable private Index mIndex;
+ 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 (mIndex != null) {
+ mInputManager.unregisterInputDeviceListener(this /* listener */);
+ }
+ mIndex = null;
+ }
+
+ synchronized void initialize(Context context, Index index) {
+ if (mIndex != null) return;
+ mIndex = index;
+ mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+ buildIndex(true /* rebuild */);
+
+ // Watch for input device changes.
+ mInputManager.registerInputDeviceListener(this /* listener */, null /* handler */);
+ }
+
+ private void buildIndex(boolean rebuild) {
+ // TODO: Fix landing page to PhysicalKeyboardFragment.
+ mIndex.updateFromClassNameResource(InputMethodAndLanguageSettings.class.getName(),
+ rebuild, true /* includeInSearchResult */);
+ }
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ buildIndex(false /* rebuild */);
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ buildIndex(true /* rebuild */);
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ buildIndex(true /* rebuild */);
+ }
+ }
+
+ // 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.
+ @Nullable private PackageManager mPackageManager;
+
+ private PackageChangeMonitor() {}
+
+ private static class SingletonHolder {
+ private static final PackageChangeMonitor INSTANCE = new PackageChangeMonitor();
+ }
+
+ static PackageChangeMonitor getInstance() {
+ return SingletonHolder.INSTANCE;
+ }
+
+ @VisibleForTesting
+ synchronized void resetForTesting() {
+ if (mPackageManager != null) {
+ unregister();
+ }
+ mPackageManager = null;
+ AccessibilityServicesMonitor.getInstance().resetForTesting();
+ InputMethodServicesMonitor.getInstance().resetForTesting();
+ }
+
+ synchronized void initialize(Context context, Index index) {
+ if (mPackageManager != null) return;;
+ mPackageManager = context.getPackageManager();
+
+ AccessibilityServicesMonitor.getInstance().initialize(context, index);
+ InputMethodServicesMonitor.getInstance().initialize(context, index);
+
+ // 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.
+ register(context, null /* thread */, UserHandle.CURRENT, false);
+ }
+
+ // Covers installed, appeared external storage with the package, upgraded.
+ @Override
+ public void onPackageAppeared(String packageName, int uid) {
+ postPackageAvailable(packageName);
+ }
+
+ // Covers uninstalled, removed external storage with the package.
+ @Override
+ public void onPackageDisappeared(String packageName, int uid) {
+ 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(() -> {
+ AccessibilityServicesMonitor.getInstance().onPackageAvailable(packageName);
+ InputMethodServicesMonitor.getInstance().onPackageAvailable(packageName);
+ }, DELAY_PROCESS_PACKAGE_CHANGE);
+ }
+
+ private void postPackageUnavailable(final String packageName) {
+ getRegisteredHandler().postDelayed(() -> {
+ AccessibilityServicesMonitor.getInstance().onPackageUnavailable(packageName);
+ InputMethodServicesMonitor.getInstance().onPackageUnavailable(packageName);
+ }, DELAY_PROCESS_PACKAGE_CHANGE);
+ }
+ }
+
+ // A singleton that holds list of available accessibility services and updates search index.
+ private static class AccessibilityServicesMonitor {
+
+ // Null if not initialized.
+ @Nullable private Index mIndex;
+ private PackageManager mPackageManager;
+ private final List<String> mAccessibilityServices = new ArrayList<>();
+
+ private AccessibilityServicesMonitor() {}
+
+ private static class SingletonHolder {
+ private static final AccessibilityServicesMonitor INSTANCE =
+ new AccessibilityServicesMonitor();
+ }
+
+ static AccessibilityServicesMonitor getInstance() {
+ return SingletonHolder.INSTANCE;
+ }
+
+ @VisibleForTesting
+ synchronized void resetForTesting() {
+ mIndex = null;
+ }
+
+ synchronized void initialize(Context context, Index index) {
+ if (mIndex != null) return;
+ mIndex = index;
+ mPackageManager = context.getPackageManager();
+ mAccessibilityServices.clear();
+ buildIndex(true /* rebuild */);
+
+ // Cache accessibility service packages to know when they go away.
+ AccessibilityManager accessibilityManager = (AccessibilityManager) context
+ .getSystemService(Context.ACCESSIBILITY_SERVICE);
+ for (final AccessibilityServiceInfo accessibilityService
+ : accessibilityManager.getInstalledAccessibilityServiceList()) {
+ ResolveInfo resolveInfo = accessibilityService.getResolveInfo();
+ if (resolveInfo != null && resolveInfo.serviceInfo != null) {
+ mAccessibilityServices.add(resolveInfo.serviceInfo.packageName);
+ }
+ }
+ }
+
+ private void buildIndex(boolean rebuild) {
+ mIndex.updateFromClassNameResource(AccessibilitySettings.class.getName(),
+ rebuild, true /* includeInSearchResult */);
+ }
+
+ synchronized void onPackageAvailable(String packageName) {
+ if (mIndex == null) return;
+ if (mAccessibilityServices.contains(packageName)) return;
+
+ final Intent intent = getAccessibilityServiceIntent(packageName);
+ final List<ResolveInfo> services = mPackageManager
+ .queryIntentServices(intent, 0 /* flags */);
+ if (services == null || services.isEmpty()) return;
+ mAccessibilityServices.add(packageName);
+ buildIndex(false /* rebuild */);
+ }
+
+ synchronized void onPackageUnavailable(String packageName) {
+ if (mIndex == null) return;
+ if (!mAccessibilityServices.remove(packageName)) return;
+ buildIndex(true /* rebuild */);
+ }
+ }
+
+ // 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 {
+
+ // Null if not initialized.
+ @Nullable private Index mIndex;
+ 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 (mIndex != null) {
+ mContentResolver.unregisterContentObserver(this /* observer */);
+ }
+ mIndex = null;
+ }
+
+ synchronized void initialize(Context context, Index index) {
+ final boolean hasFeatureIme = context.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS);
+ if (!hasFeatureIme) return;
+
+ if (mIndex != null) return;
+ mIndex = index;
+ mPackageManager = context.getPackageManager();
+ mContentResolver = context.getContentResolver();
+ mInputMethodServices.clear();
+ buildIndex(InputMethodAndLanguageSettings.class, true /* rebuild */);
+
+ // 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);
+ }
+ }
+
+ // Watch for related content URIs.
+ mContentResolver.registerContentObserver(UserDictionary.Words.CONTENT_URI,
+ true /* notifyForDescendants */, this /* observer */);
+ // TODO: Should monitor android.provider.Settings.Secure.ENABLED_INPUT_METHODS and
+ // update index of AvailableVirtualKeyboardFragment and VirtualKeyboardFragment.
+ }
+
+ private void buildIndex(Class<?> indexClass, boolean rebuild) {
+ mIndex.updateFromClassNameResource(indexClass.getName(), rebuild,
+ true /* includeInSearchResult */);
+ }
+
+ synchronized void onPackageAvailable(String packageName) {
+ if (mIndex == 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);
+ // TODO: Fix landing page to VirtualKeyboardFragment.
+ buildIndex(InputMethodAndLanguageSettings.class, false /* rebuild */);
+ }
+
+ synchronized void onPackageUnavailable(String packageName) {
+ if (mIndex == null) return;
+ if (!mInputMethodServices.remove(packageName)) return;
+ // TODO: Fix landing page to AvailableVirtualKeyboardFragment.
+ buildIndex(InputMethodAndLanguageSettings.class, true /* rebuild */);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
if (UserDictionary.Words.CONTENT_URI.equals(uri)) {
- Index.getInstance(mContext).updateFromClassNameResource(
- InputMethodAndLanguageSettings.class.getName(), true, true);
+ buildIndex(InputMethodAndLanguageSettings.class, true /* rebuild */);
}
- };
+ }
}
}