Enhance ImfUtils to InputMethodManagerWrapper class

Change-Id: I6ff86ee819a446dd3ed5f9c3400d23564027b020
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index a01c301..8bd1e52 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -19,53 +19,21 @@
 import android.content.Context;
 import android.os.IBinder;
 import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.latin.ImfUtils;
 
 import java.lang.reflect.Method;
 
-// TODO: Override this class with the concrete implementation if we need to take care of the
-// performance.
 public final class InputMethodManagerCompatWrapper {
-    private static final String TAG = InputMethodManagerCompatWrapper.class.getSimpleName();
     private static final Method METHOD_switchToNextInputMethod = CompatUtils.getMethod(
             InputMethodManager.class, "switchToNextInputMethod", IBinder.class, Boolean.TYPE);
 
-    private static final InputMethodManagerCompatWrapper sInstance =
-            new InputMethodManagerCompatWrapper();
+    public final InputMethodManager mImm;
 
-    private InputMethodManager mImm;
-
-    private InputMethodManagerCompatWrapper() {
-        // This wrapper class is not publicly instantiable.
+    public InputMethodManagerCompatWrapper(final Context context) {
+        mImm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
     }
 
-    public static InputMethodManagerCompatWrapper getInstance() {
-        if (sInstance.mImm == null) {
-            throw new RuntimeException(TAG + ".getInstance() is called before initialization");
-        }
-        return sInstance;
-    }
-
-    public static void init(Context context) {
-        sInstance.mImm = ImfUtils.getInputMethodManager(context);
-    }
-
-    public InputMethodSubtype getLastInputMethodSubtype() {
-        return mImm.getLastInputMethodSubtype();
-    }
-
-    public boolean switchToLastInputMethod(IBinder token) {
-        return mImm.switchToLastInputMethod(token);
-    }
-
-    public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
+    public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
         return (Boolean)CompatUtils.invoke(mImm, false, METHOD_switchToNextInputMethod, token,
                 onlyCurrentIme);
     }
-
-    public void showInputMethodPicker() {
-        mImm.showInputMethodPicker();
-    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 926f3ec..c2e31ee 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -30,11 +30,11 @@
 import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
 import com.android.inputmethod.keyboard.internal.KeyboardState;
 import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
-import com.android.inputmethod.latin.ImfUtils;
 import com.android.inputmethod.latin.InputView;
 import com.android.inputmethod.latin.LatinIME;
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.RichInputMethodManager;
 import com.android.inputmethod.latin.SettingsValues;
 import com.android.inputmethod.latin.SubtypeSwitcher;
 import com.android.inputmethod.latin.WordComposer;
@@ -142,7 +142,7 @@
         builder.setOptions(
                 settingsValues.isVoiceKeyEnabled(editorInfo),
                 settingsValues.isVoiceKeyOnMain(),
-                settingsValues.isLanguageSwitchKeyEnabled(mThemeContext));
+                settingsValues.isLanguageSwitchKeyEnabled());
         mKeyboardLayoutSet = builder.build();
         try {
             mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols));
@@ -187,7 +187,7 @@
         final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage(
                 keyboard.mId.mLocale);
         keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage,
-                ImfUtils.hasMultipleEnabledIMEsOrSubtypes(mLatinIME, true));
+                RichInputMethodManager.getInstance().hasMultipleEnabledIMEsOrSubtypes(true));
     }
 
     public Keyboard getKeyboard() {
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
index d126077..a071bc9 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
@@ -50,6 +50,7 @@
 import java.util.TreeSet;
 
 public final class AdditionalSubtypeSettings extends PreferenceFragment {
+    private RichInputMethodManager mRichImm;
     private SharedPreferences mPrefs;
     private SubtypeLocaleAdapter mSubtypeLocaleAdapter;
     private KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter;
@@ -63,6 +64,7 @@
     private static final String KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN =
             "is_subtype_enabler_notification_dialog_open";
     private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler";
+
     static final class SubtypeLocaleItem extends Pair<String, String>
             implements Comparable<SubtypeLocaleItem> {
         public SubtypeLocaleItem(final String localeString, final String displayName) {
@@ -90,7 +92,8 @@
             setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 
             final TreeSet<SubtypeLocaleItem> items = CollectionUtils.newTreeSet();
-            final InputMethodInfo imi = ImfUtils.getInputMethodInfoOfThisIme(context);
+            final InputMethodInfo imi = RichInputMethodManager.getInstance()
+                    .getInputMethodInfoOfThisIme();
             final int count = imi.getSubtypeCount();
             for (int i = 0; i < count; i++) {
                 final InputMethodSubtype subtype = imi.getSubtypeAt(i);
@@ -375,6 +378,8 @@
     public void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        RichInputMethodManager.init(getActivity());
+        mRichImm = RichInputMethodManager.getInstance();
         addPreferencesFromResource(R.xml.additional_subtype_settings);
         setHasOptionsMenu(true);
 
@@ -431,7 +436,7 @@
             mIsAddingNewSubtype = false;
             final PreferenceGroup group = getPreferenceScreen();
             group.removePreference(subtypePref);
-            ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes());
+            mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
         }
 
         @Override
@@ -441,7 +446,7 @@
                 return;
             }
             if (findDuplicatedSubtype(subtype) == null) {
-                ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes());
+                mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
                 return;
             }
 
@@ -458,7 +463,7 @@
             mIsAddingNewSubtype = false;
             final InputMethodSubtype subtype = subtypePref.getSubtype();
             if (findDuplicatedSubtype(subtype) == null) {
-                ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes());
+                mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
                 mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey();
                 mSubtypeEnablerNotificationDialog = createDialog(subtypePref);
                 mSubtypeEnablerNotificationDialog.show();
@@ -493,8 +498,8 @@
     private InputMethodSubtype findDuplicatedSubtype(final InputMethodSubtype subtype) {
         final String localeString = subtype.getLocale();
         final String keyboardLayoutSetName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
-        return ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                getActivity(), localeString, keyboardLayoutSetName);
+        return mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                localeString, keyboardLayoutSetName);
     }
 
     private AlertDialog createDialog(
@@ -507,7 +512,7 @@
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
                         final Intent intent = CompatUtils.getInputLanguageSelectionIntent(
-                                ImfUtils.getInputMethodIdOfThisIme(getActivity()),
+                                mRichImm.getInputMethodIdOfThisIme(),
                                 Intent.FLAG_ACTIVITY_NEW_TASK
                                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
                                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -565,7 +570,7 @@
         } finally {
             editor.apply();
         }
-        ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), subtypes);
+        mRichImm.setAdditionalInputMethodSubtypes(subtypes);
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/ImfUtils.java b/java/src/com/android/inputmethod/latin/ImfUtils.java
deleted file mode 100644
index 2674e45..0000000
--- a/java/src/com/android/inputmethod/latin/ImfUtils.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2012 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.inputmethod.latin;
-
-import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE;
-
-import android.content.Context;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Utility class for Input Method Framework
- */
-public final class ImfUtils {
-    private ImfUtils() {
-        // This utility class is not publicly instantiable.
-    }
-
-    private static InputMethodManager sInputMethodManager;
-
-    public static InputMethodManager getInputMethodManager(Context context) {
-        if (sInputMethodManager == null) {
-            sInputMethodManager = (InputMethodManager)context.getSystemService(
-                    Context.INPUT_METHOD_SERVICE);
-        }
-        return sInputMethodManager;
-    }
-
-    private static InputMethodInfo sInputMethodInfoOfThisIme;
-
-    public static InputMethodInfo getInputMethodInfoOfThisIme(Context context) {
-        if (sInputMethodInfoOfThisIme == null) {
-            final InputMethodManager imm = getInputMethodManager(context);
-            final String packageName = context.getPackageName();
-            for (final InputMethodInfo imi : imm.getInputMethodList()) {
-                if (imi.getPackageName().equals(packageName))
-                    return imi;
-            }
-            throw new RuntimeException("Can not find input method id for " + packageName);
-        }
-        return sInputMethodInfoOfThisIme;
-    }
-
-    public static String getInputMethodIdOfThisIme(Context context) {
-        return getInputMethodInfoOfThisIme(context).getId();
-    }
-
-    public static boolean checkIfSubtypeBelongsToThisImeAndEnabled(Context context,
-            InputMethodSubtype ims) {
-        final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context);
-        final InputMethodManager imm = getInputMethodManager(context);
-        // TODO: Cache all subtypes of this IME for optimization
-        final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(myImi, true);
-        for (final InputMethodSubtype subtype : subtypes) {
-            if (subtype.equals(ims)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public static boolean checkIfSubtypeBelongsToThisIme(Context context,
-            InputMethodSubtype ims) {
-        final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context);
-        final int count = myImi.getSubtypeCount();
-        for (int i = 0; i < count; i++) {
-            final InputMethodSubtype subtype = myImi.getSubtypeAt(i);
-            if (subtype.equals(ims)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public static InputMethodSubtype getCurrentInputMethodSubtype(Context context,
-            InputMethodSubtype defaultSubtype) {
-        final InputMethodManager imm = getInputMethodManager(context);
-        final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype();
-        return (currentSubtype != null) ? currentSubtype : defaultSubtype;
-    }
-
-    public static boolean hasMultipleEnabledIMEsOrSubtypes(Context context,
-            final boolean shouldIncludeAuxiliarySubtypes) {
-        final InputMethodManager imm = getInputMethodManager(context);
-        final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
-        return hasMultipleEnabledSubtypes(context, shouldIncludeAuxiliarySubtypes, enabledImis);
-    }
-
-    public static boolean hasMultipleEnabledSubtypesInThisIme(Context context,
-            final boolean shouldIncludeAuxiliarySubtypes) {
-        final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context);
-        final List<InputMethodInfo> imiList = Collections.singletonList(myImi);
-        return hasMultipleEnabledSubtypes(context, shouldIncludeAuxiliarySubtypes, imiList);
-    }
-
-    private static boolean hasMultipleEnabledSubtypes(Context context,
-            final boolean shouldIncludeAuxiliarySubtypes, List<InputMethodInfo> imiList) {
-        final InputMethodManager imm = getInputMethodManager(context);
-
-        // Number of the filtered IMEs
-        int filteredImisCount = 0;
-
-        for (InputMethodInfo imi : imiList) {
-            // We can return true immediately after we find two or more filtered IMEs.
-            if (filteredImisCount > 1) return true;
-            final List<InputMethodSubtype> subtypes =
-                    imm.getEnabledInputMethodSubtypeList(imi, true);
-            // IMEs that have no subtypes should be counted.
-            if (subtypes.isEmpty()) {
-                ++filteredImisCount;
-                continue;
-            }
-
-            int auxCount = 0;
-            for (InputMethodSubtype subtype : subtypes) {
-                if (subtype.isAuxiliary()) {
-                    ++auxCount;
-                }
-            }
-            final int nonAuxCount = subtypes.size() - auxCount;
-
-            // IMEs that have one or more non-auxiliary subtypes should be counted.
-            // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
-            // subtypes should be counted as well.
-            if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
-                ++filteredImisCount;
-                continue;
-            }
-        }
-
-        if (filteredImisCount > 1) {
-            return true;
-        }
-        final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(null, true);
-        int keyboardCount = 0;
-        // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's
-        // both explicitly and implicitly enabled input method subtype.
-        // (The current IME should be LatinIME.)
-        for (InputMethodSubtype subtype : subtypes) {
-            if (KEYBOARD_MODE.equals(subtype.getMode())) {
-                ++keyboardCount;
-            }
-        }
-        return keyboardCount > 1;
-    }
-
-    public static InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(
-            Context context, String localeString, String keyboardLayoutSetName) {
-        final InputMethodInfo imi = getInputMethodInfoOfThisIme(context);
-        final int count = imi.getSubtypeCount();
-        for (int i = 0; i < count; i++) {
-            final InputMethodSubtype subtype = imi.getSubtypeAt(i);
-            final String layoutName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
-            if (localeString.equals(subtype.getLocale())
-                    && keyboardLayoutSetName.equals(layoutName)) {
-                return subtype;
-            }
-        }
-        return null;
-    }
-
-    public static void setAdditionalInputMethodSubtypes(Context context,
-            InputMethodSubtype[] subtypes) {
-        final InputMethodManager imm = getInputMethodManager(context);
-        final String imiId = getInputMethodIdOfThisIme(context);
-        imm.setAdditionalInputMethodSubtypes(imiId, subtypes);
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 5f87c8c..049d134 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -56,13 +56,13 @@
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.inputmethod.accessibility.AccessibilityUtils;
 import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.compat.CompatUtils;
-import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
 import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
 import com.android.inputmethod.compat.SuggestionSpanUtils;
 import com.android.inputmethod.keyboard.KeyDetector;
@@ -137,7 +137,7 @@
     private CompletionInfo[] mApplicationSpecifiedCompletions;
     private ApplicationInfo mTargetApplicationInfo;
 
-    private InputMethodManagerCompatWrapper mImm;
+    private RichInputMethodManager mRichImm;
     private Resources mResources;
     private SharedPreferences mPrefs;
     @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
@@ -381,14 +381,14 @@
         if (ProductionFlag.IS_EXPERIMENTAL) {
             ResearchLogger.getInstance().init(this, prefs);
         }
-        InputMethodManagerCompatWrapper.init(this);
+        RichInputMethodManager.init(this);
         SubtypeSwitcher.init(this);
         KeyboardSwitcher.init(this, prefs);
         AccessibilityUtils.init(this);
 
         super.onCreate();
 
-        mImm = InputMethodManagerCompatWrapper.getInstance();
+        mRichImm = RichInputMethodManager.getInstance();
         mHandler.onCreate();
         DEBUG = LatinImeLogger.sDBG;
 
@@ -397,7 +397,7 @@
 
         loadSettings();
 
-        ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes());
+        mRichImm.setAdditionalInputMethodSubtypes(mCurrentSettings.getAdditionalSubtypes());
 
         initSuggest();
 
@@ -548,7 +548,7 @@
     @Override
     public void onConfigurationChanged(final Configuration conf) {
         // System locale has been changed. Needs to reload keyboard.
-        if (mSubtypeSwitcher.onConfigurationChanged(conf, this)) {
+        if (mSubtypeSwitcher.onConfigurationChanged(conf)) {
             loadKeyboard();
         }
         // If orientation changed while predicting, commit the change
@@ -690,8 +690,8 @@
                     .updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
             if (!currentSubtypeEnabled) {
                 // Current subtype is disabled. Needs to update subtype and keyboard.
-                final InputMethodSubtype newSubtype = ImfUtils.getCurrentInputMethodSubtype(
-                        this, mSubtypeSwitcher.getNoLanguageSubtype());
+                final InputMethodSubtype newSubtype = mRichImm.getCurrentInputMethodSubtype(
+                        mSubtypeSwitcher.getNoLanguageSubtype());
                 mSubtypeSwitcher.updateSubtype(newSubtype);
                 loadKeyboard();
             }
@@ -1208,9 +1208,8 @@
         if (isShowingOptionDialog()) return false;
         switch (requestCode) {
         case CODE_SHOW_INPUT_METHOD_PICKER:
-            if (ImfUtils.hasMultipleEnabledIMEsOrSubtypes(
-                    this, true /* include aux subtypes */)) {
-                mImm.showInputMethodPicker();
+            if (mRichImm.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) {
+                mRichImm.getInputMethodManager().showInputMethodPicker();
                 return true;
             }
             return false;
@@ -1234,21 +1233,22 @@
     private void handleLanguageSwitchKey() {
         final IBinder token = getWindow().getWindow().getAttributes().token;
         if (mCurrentSettings.mIncludesOtherImesInLanguageSwitchList) {
-            mImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
+            mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
             return;
         }
         if (mShouldSwitchToLastSubtype) {
-            final InputMethodSubtype lastSubtype = mImm.getLastInputMethodSubtype();
+            final InputMethodManager imm = mRichImm.getInputMethodManager();
+            final InputMethodSubtype lastSubtype = imm.getLastInputMethodSubtype();
             final boolean lastSubtypeBelongsToThisIme =
-                    ImfUtils.checkIfSubtypeBelongsToThisImeAndEnabled(this, lastSubtype);
-            if (lastSubtypeBelongsToThisIme && mImm.switchToLastInputMethod(token)) {
+                    mRichImm.checkIfSubtypeBelongsToThisImeAndEnabled(lastSubtype);
+            if (lastSubtypeBelongsToThisIme && imm.switchToLastInputMethod(token)) {
                 mShouldSwitchToLastSubtype = false;
             } else {
-                mImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
+                mRichImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
                 mShouldSwitchToLastSubtype = true;
             }
         } else {
-            mImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
+            mRichImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
         }
     }
 
@@ -2345,7 +2345,6 @@
                 getString(R.string.language_selection_title),
                 getString(R.string.english_ime_settings),
         };
-        final Context context = this;
         final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface di, int position) {
@@ -2353,7 +2352,7 @@
                 switch (position) {
                 case 0:
                     Intent intent = CompatUtils.getInputLanguageSelectionIntent(
-                            ImfUtils.getInputMethodIdOfThisIme(context),
+                            mRichImm.getInputMethodIdOfThisIme(),
                             Intent.FLAG_ACTIVITY_NEW_TASK
                             | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
                             | Intent.FLAG_ACTIVITY_CLEAR_TOP);
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
new file mode 100644
index 0000000..d7055f7
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2012 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.inputmethod.latin;
+
+import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Enrichment class for InputMethodManager to simplify interaction and add functionality.
+ */
+public final class RichInputMethodManager {
+    private static final String TAG = RichInputMethodManager.class.getSimpleName();
+
+    private RichInputMethodManager() {
+        // This utility class is not publicly instantiable.
+    }
+
+    private static final RichInputMethodManager sInstance = new RichInputMethodManager();
+
+    private InputMethodManagerCompatWrapper mImmWrapper;
+    private InputMethodInfo mInputMethodInfoOfThisIme;
+
+    public static RichInputMethodManager getInstance() {
+        sInstance.checkInitialized();
+        return sInstance;
+    }
+
+    public static void init(final Context context) {
+        sInstance.initInternal(context);
+    }
+
+    private void checkInitialized() {
+        if (mImmWrapper == null) {
+            throw new RuntimeException(TAG + " is used before initialization");
+        }
+    }
+
+    private void initInternal(final Context context) {
+        mImmWrapper = new InputMethodManagerCompatWrapper(context);
+        mInputMethodInfoOfThisIme = getInputMethodInfoOfThisIme(context);
+    }
+
+    public InputMethodManager getInputMethodManager() {
+        checkInitialized();
+        return mImmWrapper.mImm;
+    }
+
+    private InputMethodInfo getInputMethodInfoOfThisIme(final Context context) {
+        final String packageName = context.getPackageName();
+        for (final InputMethodInfo imi : mImmWrapper.mImm.getInputMethodList()) {
+            if (imi.getPackageName().equals(packageName)) {
+                return imi;
+            }
+        }
+        throw new RuntimeException("Input method id for " + packageName + " not found.");
+    }
+
+    public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
+        final boolean result = mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme);
+        if (!result) {
+            mImmWrapper.mImm.switchToLastInputMethod(token);
+            return false;
+        }
+        return true;
+    }
+
+    public InputMethodInfo getInputMethodInfoOfThisIme() {
+        return mInputMethodInfoOfThisIme;
+    }
+
+    public String getInputMethodIdOfThisIme() {
+        return mInputMethodInfoOfThisIme.getId();
+    }
+
+    public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype ims) {
+        return checkIfSubtypeBelongsToImeAndEnabled(mInputMethodInfoOfThisIme, ims);
+    }
+
+    public boolean checkIfSubtypeBelongsToImeAndEnabled(final InputMethodInfo imi,
+            final InputMethodSubtype ims) {
+        final List<InputMethodSubtype> subtypes = mImmWrapper.mImm.getEnabledInputMethodSubtypeList(
+                imi, true /* allowsImplicitlySelectedSubtypes */);
+        for (final InputMethodSubtype subtype : subtypes) {
+            if (subtype.equals(ims)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean checkIfSubtypeBelongsToThisIme(final InputMethodSubtype ims) {
+        final InputMethodInfo myImi = mInputMethodInfoOfThisIme;
+        final int count = myImi.getSubtypeCount();
+        for (int i = 0; i < count; i++) {
+            final InputMethodSubtype subtype = myImi.getSubtypeAt(i);
+            if (subtype.equals(ims)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public InputMethodSubtype getCurrentInputMethodSubtype(
+            final InputMethodSubtype defaultSubtype) {
+        final InputMethodSubtype currentSubtype = mImmWrapper.mImm.getCurrentInputMethodSubtype();
+        return (currentSubtype != null) ? currentSubtype : defaultSubtype;
+    }
+
+    public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) {
+        final List<InputMethodInfo> enabledImis = mImmWrapper.mImm.getEnabledInputMethodList();
+        return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, enabledImis);
+    }
+
+    public boolean hasMultipleEnabledSubtypesInThisIme(
+            final boolean shouldIncludeAuxiliarySubtypes) {
+        final List<InputMethodInfo> imiList = Collections.singletonList(mInputMethodInfoOfThisIme);
+        return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList);
+    }
+
+    private boolean hasMultipleEnabledSubtypes(final boolean shouldIncludeAuxiliarySubtypes,
+            final List<InputMethodInfo> imiList) {
+        // Number of the filtered IMEs
+        int filteredImisCount = 0;
+
+        for (InputMethodInfo imi : imiList) {
+            // We can return true immediately after we find two or more filtered IMEs.
+            if (filteredImisCount > 1) return true;
+            final List<InputMethodSubtype> subtypes =
+                    mImmWrapper.mImm.getEnabledInputMethodSubtypeList(imi, true);
+            // IMEs that have no subtypes should be counted.
+            if (subtypes.isEmpty()) {
+                ++filteredImisCount;
+                continue;
+            }
+
+            int auxCount = 0;
+            for (InputMethodSubtype subtype : subtypes) {
+                if (subtype.isAuxiliary()) {
+                    ++auxCount;
+                }
+            }
+            final int nonAuxCount = subtypes.size() - auxCount;
+
+            // IMEs that have one or more non-auxiliary subtypes should be counted.
+            // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
+            // subtypes should be counted as well.
+            if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
+                ++filteredImisCount;
+                continue;
+            }
+        }
+
+        if (filteredImisCount > 1) {
+            return true;
+        }
+        final List<InputMethodSubtype> subtypes =
+                mImmWrapper.mImm.getEnabledInputMethodSubtypeList(null, true);
+        int keyboardCount = 0;
+        // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's
+        // both explicitly and implicitly enabled input method subtype.
+        // (The current IME should be LatinIME.)
+        for (InputMethodSubtype subtype : subtypes) {
+            if (KEYBOARD_MODE.equals(subtype.getMode())) {
+                ++keyboardCount;
+            }
+        }
+        return keyboardCount > 1;
+    }
+
+    public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final String localeString,
+            final String keyboardLayoutSetName) {
+        final InputMethodInfo myImi = mInputMethodInfoOfThisIme;
+        final int count = myImi.getSubtypeCount();
+        for (int i = 0; i < count; i++) {
+            final InputMethodSubtype subtype = myImi.getSubtypeAt(i);
+            final String layoutName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
+            if (localeString.equals(subtype.getLocale())
+                    && keyboardLayoutSetName.equals(layoutName)) {
+                return subtype;
+            }
+        }
+        return null;
+    }
+
+    public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) {
+        mImmWrapper.mImm.setAdditionalInputMethodSubtypes(
+                mInputMethodInfoOfThisIme.getId(), subtypes);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 2a778aa..61536b5 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -355,16 +355,15 @@
         return prefs.getBoolean(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, true);
     }
 
-    public boolean isLanguageSwitchKeyEnabled(final Context context) {
+    public boolean isLanguageSwitchKeyEnabled() {
         if (!mShowsLanguageSwitchKey) {
             return false;
         }
+        final RichInputMethodManager imm = RichInputMethodManager.getInstance();
         if (mIncludesOtherImesInLanguageSwitchList) {
-            return ImfUtils.hasMultipleEnabledIMEsOrSubtypes(
-                    context, /* include aux subtypes */false);
+            return imm.hasMultipleEnabledIMEsOrSubtypes(false /* include aux subtypes */);
         } else {
-            return ImfUtils.hasMultipleEnabledSubtypesInThisIme(
-                    context, /* include aux subtypes */false);
+            return imm.hasMultipleEnabledSubtypesInThisIme(false /* include aux subtypes */);
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 8e51a37..0f339eb 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -43,7 +43,7 @@
     private static final String TAG = SubtypeSwitcher.class.getSimpleName();
 
     private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
-    private /* final */ InputMethodManager mImm;
+    private /* final */ RichInputMethodManager mRichImm;
     private /* final */ Resources mResources;
     private /* final */ ConnectivityManager mConnectivityManager;
 
@@ -84,7 +84,7 @@
     public static void init(final Context context) {
         SubtypeLocale.init(context);
         sInstance.initialize(context);
-        sInstance.updateAllParameters(context);
+        sInstance.updateAllParameters();
     }
 
     private SubtypeSwitcher() {
@@ -93,13 +93,13 @@
 
     private void initialize(final Context service) {
         mResources = service.getResources();
-        mImm = ImfUtils.getInputMethodManager(service);
+        mRichImm = RichInputMethodManager.getInstance();
         mConnectivityManager = (ConnectivityManager) service.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
         mCurrentSystemLocale = mResources.getConfiguration().locale;
-        mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
-        mCurrentSubtype = ImfUtils.getCurrentInputMethodSubtype(service, mNoLanguageSubtype);
+        mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
+        mCurrentSubtype = mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype);
         if (mNoLanguageSubtype == null) {
             throw new RuntimeException("Can't find no lanugage with QWERTY subtype");
         }
@@ -110,9 +110,9 @@
 
     // Update all parameters stored in SubtypeSwitcher.
     // Only configuration changed event is allowed to call this because this is heavy.
-    private void updateAllParameters(final Context context) {
+    private void updateAllParameters() {
         mCurrentSystemLocale = mResources.getConfiguration().locale;
-        updateSubtype(ImfUtils.getCurrentInputMethodSubtype(context, mNoLanguageSubtype));
+        updateSubtype(mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype));
         updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
     }
 
@@ -137,7 +137,7 @@
      */
     private boolean updateEnabledSubtypesAndReturnIfEnabled(final InputMethodSubtype subtype) {
         final List<InputMethodSubtype> enabledSubtypesOfThisIme =
-                mImm.getEnabledInputMethodSubtypeList(null, true);
+                mRichImm.getInputMethodManager().getEnabledInputMethodSubtypeList(null, true);
         mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size());
 
         for (final InputMethodSubtype ims : enabledSubtypesOfThisIme) {
@@ -162,7 +162,7 @@
         }
         // TODO: Update an icon for shortcut IME
         final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
-                mImm.getShortcutInputMethodsAndSubtypes();
+                mRichImm.getInputMethodManager().getShortcutInputMethodsAndSubtypes();
         mShortcutInputMethodInfo = null;
         mShortcutSubtype = null;
         for (final InputMethodInfo imi : shortcuts.keySet()) {
@@ -221,7 +221,7 @@
         if (token == null) {
             return;
         }
-        final InputMethodManager imm = mImm;
+        final InputMethodManager imm = mRichImm.getInputMethodManager();
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
@@ -238,14 +238,8 @@
         if (mShortcutSubtype == null) {
             return true;
         }
-        final boolean allowsImplicitlySelectedSubtypes = true;
-        for (final InputMethodSubtype enabledSubtype : mImm.getEnabledInputMethodSubtypeList(
-                mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) {
-            if (enabledSubtype.equals(mShortcutSubtype)) {
-                return true;
-            }
-        }
-        return false;
+        return mRichImm.checkIfSubtypeBelongsToImeAndEnabled(
+                mShortcutInputMethodInfo, mShortcutSubtype);
     }
 
     public boolean isShortcutImeReady() {
@@ -285,12 +279,12 @@
         return SubtypeLocale.getSubtypeLocale(mCurrentSubtype);
     }
 
-    public boolean onConfigurationChanged(final Configuration conf, final Context context) {
+    public boolean onConfigurationChanged(final Configuration conf) {
         final Locale systemLocale = conf.locale;
         final boolean systemLocaleChanged = !systemLocale.equals(mCurrentSystemLocale);
         // If system configuration was changed, update all parameters.
         if (systemLocaleChanged) {
-            updateAllParameters(context);
+            updateAllParameters();
         }
         return systemLocaleChanged;
     }
diff --git a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
index bc50439..015c271 100644
--- a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
@@ -23,7 +23,7 @@
 
 import com.android.inputmethod.latin.AdditionalSubtype;
 import com.android.inputmethod.latin.CollectionUtils;
-import com.android.inputmethod.latin.ImfUtils;
+import com.android.inputmethod.latin.RichInputMethodManager;
 import com.android.inputmethod.latin.StringUtils;
 import com.android.inputmethod.latin.SubtypeLocale;
 
@@ -34,12 +34,15 @@
     // Locale to subtypes list.
     private final ArrayList<InputMethodSubtype> mSubtypesList = CollectionUtils.newArrayList();
 
+    private RichInputMethodManager mRichImm;
     private Resources mRes;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         final Context context = getContext();
+        RichInputMethodManager.init(context);
+        mRichImm = RichInputMethodManager.getInstance();
         mRes = context.getResources();
         SubtypeLocale.init(context);
     }
@@ -103,19 +106,18 @@
     //  zz    azerty T      AZERTY    AZERTY
 
     public void testPredefinedSubtypes() {
-        final Context context = getContext();
-        final InputMethodSubtype EN_US = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.US.toString(), "qwerty");
-        final InputMethodSubtype EN_GB = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.UK.toString(), "qwerty");
-        final InputMethodSubtype FR = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.FRENCH.toString(), "azerty");
-        final InputMethodSubtype FR_CA = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.CANADA_FRENCH.toString(), "qwerty");
-        final InputMethodSubtype DE = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.GERMAN.toString(), "qwertz");
-        final InputMethodSubtype ZZ = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, SubtypeLocale.NO_LANGUAGE, "qwerty");
+        final InputMethodSubtype EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.US.toString(), "qwerty");
+        final InputMethodSubtype EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.UK.toString(), "qwerty");
+        final InputMethodSubtype FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.FRENCH.toString(), "azerty");
+        final InputMethodSubtype FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.CANADA_FRENCH.toString(), "qwerty");
+        final InputMethodSubtype DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.GERMAN.toString(), "qwertz");
+        final InputMethodSubtype ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                SubtypeLocale.NO_LANGUAGE, "qwerty");
 
         assertEquals("en_US", "English (US)",
                 MainKeyboardView.getFullDisplayName(EN_US, mRes));
diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
index 52a3745..0a48f0d 100644
--- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
+++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
@@ -30,12 +30,15 @@
     // Locale to subtypes list.
     private final ArrayList<InputMethodSubtype> mSubtypesList = CollectionUtils.newArrayList();
 
+    private RichInputMethodManager mRichImm;
     private Resources mRes;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         final Context context = getContext();
+        RichInputMethodManager.init(context);
+        mRichImm = RichInputMethodManager.getInstance();
         mRes = context.getResources();
         SubtypeLocale.init(context);
     }
@@ -70,19 +73,18 @@
     //  zz    azerty T  No language (AZERTY)    in system locale
 
     public void testPredefinedSubtypesInEnglish() {
-        final Context context = getContext();
-        final InputMethodSubtype EN_US = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.US.toString(), "qwerty");
-        final InputMethodSubtype EN_GB = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.UK.toString(), "qwerty");
-        final InputMethodSubtype FR = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.FRENCH.toString(), "azerty");
-        final InputMethodSubtype FR_CA = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.CANADA_FRENCH.toString(), "qwerty");
-        final InputMethodSubtype DE = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.GERMAN.toString(), "qwertz");
-        final InputMethodSubtype ZZ = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, SubtypeLocale.NO_LANGUAGE, "qwerty");
+        final InputMethodSubtype EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.US.toString(), "qwerty");
+        final InputMethodSubtype EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.UK.toString(), "qwerty");
+        final InputMethodSubtype FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.FRENCH.toString(), "azerty");
+        final InputMethodSubtype FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.CANADA_FRENCH.toString(), "qwerty");
+        final InputMethodSubtype DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.GERMAN.toString(), "qwertz");
+        final InputMethodSubtype ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                SubtypeLocale.NO_LANGUAGE, "qwerty");
 
         assertEquals("en_US", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_US));
         assertEquals("en_GB", "qwerty", SubtypeLocale.getKeyboardLayoutSetName(EN_GB));
@@ -140,19 +142,18 @@
     }
 
     public void testPredefinedSubtypesInFrench() {
-        final Context context = getContext();
-        final InputMethodSubtype EN_US = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.US.toString(), "qwerty");
-        final InputMethodSubtype EN_GB = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.UK.toString(), "qwerty");
-        final InputMethodSubtype FR = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.FRENCH.toString(), "azerty");
-        final InputMethodSubtype FR_CA = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.CANADA_FRENCH.toString(), "qwerty");
-        final InputMethodSubtype DE = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.GERMAN.toString(), "qwertz");
-        final InputMethodSubtype ZZ = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, SubtypeLocale.NO_LANGUAGE, "qwerty");
+        final InputMethodSubtype EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.US.toString(), "qwerty");
+        final InputMethodSubtype EN_GB = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.UK.toString(), "qwerty");
+        final InputMethodSubtype FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.FRENCH.toString(), "azerty");
+        final InputMethodSubtype FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.CANADA_FRENCH.toString(), "qwerty");
+        final InputMethodSubtype DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.GERMAN.toString(), "qwertz");
+        final InputMethodSubtype ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                SubtypeLocale.NO_LANGUAGE, "qwerty");
 
         final RunInLocale<Void> tests = new RunInLocale<Void>() {
             @Override