aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/compat
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/compat')
-rw-r--r--java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java39
-rw-r--r--java/src/com/android/inputmethod/compat/CompatUtils.java156
-rw-r--r--java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java102
-rw-r--r--java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java80
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java59
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java227
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java84
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java155
-rw-r--r--java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java118
-rw-r--r--java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java78
-rw-r--r--java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java47
11 files changed, 1145 insertions, 0 deletions
diff --git a/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java b/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java
new file mode 100644
index 000000000..65949357f
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/AbstractCompatWrapper.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import android.util.Log;
+
+public abstract class AbstractCompatWrapper {
+ private static final String TAG = AbstractCompatWrapper.class.getSimpleName();
+ protected final Object mObj;
+
+ public AbstractCompatWrapper(Object obj) {
+ if (obj == null) {
+ Log.e(TAG, "Invalid input to AbstructCompatWrapper");
+ }
+ mObj = obj;
+ }
+
+ public Object getOriginalObject() {
+ return mObj;
+ }
+
+ public boolean hasOriginalObject() {
+ return mObj != null;
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java
new file mode 100644
index 000000000..6e14bfa8b
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/CompatUtils.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CompatUtils {
+ private static final String TAG = CompatUtils.class.getSimpleName();
+ private static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
+ // TODO: Can these be constants instead of literal String constants?
+ private static final String INPUT_METHOD_SUBTYPE_SETTINGS =
+ "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
+ private static final String INPUT_LANGUAGE_SELECTION =
+ "com.android.inputmethod.latin.INPUT_LANGUAGE_SELECTION";
+
+ public static Intent getInputLanguageSelectionIntent(String inputMethodId,
+ int flagsForSubtypeSettings) {
+ final String action;
+ Intent intent;
+ if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED
+ /* android.os.Build.VERSION_CODES.HONEYCOMB */
+ && android.os.Build.VERSION.SDK_INT >= 11) {
+ // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS
+ action = INPUT_METHOD_SUBTYPE_SETTINGS;
+ intent = new Intent(action);
+ if (!TextUtils.isEmpty(inputMethodId)) {
+ intent.putExtra(EXTRA_INPUT_METHOD_ID, inputMethodId);
+ }
+ if (flagsForSubtypeSettings > 0) {
+ intent.setFlags(flagsForSubtypeSettings);
+ }
+ } else {
+ action = INPUT_LANGUAGE_SELECTION;
+ intent = new Intent(action);
+ }
+ return intent;
+ }
+
+ public static Class<?> getClass(String className) {
+ try {
+ return Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ public static Method getMethod(Class<?> targetClass, String name,
+ Class<?>... parameterTypes) {
+ if (targetClass == null || TextUtils.isEmpty(name)) return null;
+ try {
+ return targetClass.getMethod(name, parameterTypes);
+ } catch (SecurityException e) {
+ // ignore
+ } catch (NoSuchMethodException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ public static Field getField(Class<?> targetClass, String name) {
+ if (targetClass == null || TextUtils.isEmpty(name)) return null;
+ try {
+ return targetClass.getField(name);
+ } catch (SecurityException e) {
+ // ignore
+ } catch (NoSuchFieldException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ public static Constructor<?> getConstructor(Class<?> targetClass, Class<?>[] types) {
+ if (targetClass == null || types == null) return null;
+ try {
+ return targetClass.getConstructor(types);
+ } catch (SecurityException e) {
+ // ignore
+ } catch (NoSuchMethodException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ public static Object newInstance(Constructor<?> constructor, Object[] args) {
+ if (constructor == null) return null;
+ try {
+ return constructor.newInstance(args);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in newInstance: " + e.getClass().getSimpleName());
+ }
+ return null;
+ }
+
+ public static Object invoke(
+ Object receiver, Object defaultValue, Method method, Object... args) {
+ if (method == null) return defaultValue;
+ try {
+ return method.invoke(receiver, args);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in invoke: " + e.getClass().getSimpleName());
+ }
+ return defaultValue;
+ }
+
+ public static Object getFieldValue(Object receiver, Object defaultValue, Field field) {
+ if (field == null) return defaultValue;
+ try {
+ return field.get(receiver);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in getFieldValue: " + e.getClass().getSimpleName());
+ }
+ return defaultValue;
+ }
+
+ public static void setFieldValue(Object receiver, Field field, Object value) {
+ if (field == null) return;
+ try {
+ field.set(receiver, value);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in setFieldValue: " + e.getClass().getSimpleName());
+ }
+ }
+
+ public static List<InputMethodSubtypeCompatWrapper> copyInputMethodSubtypeListToWrapper(
+ Object listObject) {
+ if (!(listObject instanceof List<?>)) return null;
+ final List<InputMethodSubtypeCompatWrapper> subtypes =
+ new ArrayList<InputMethodSubtypeCompatWrapper>();
+ for (Object o: (List<?>)listObject) {
+ subtypes.add(new InputMethodSubtypeCompatWrapper(o));
+ }
+ return subtypes;
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
new file mode 100644
index 000000000..2789bcb39
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
+import java.lang.reflect.Field;
+
+public class EditorInfoCompatUtils {
+ private static final Field FIELD_IME_FLAG_NAVIGATE_NEXT = CompatUtils.getField(
+ EditorInfo.class, "IME_FLAG_NAVIGATE_NEXT");
+ private static final Field FIELD_IME_FLAG_NAVIGATE_PREVIOUS = CompatUtils.getField(
+ EditorInfo.class, "IME_FLAG_NAVIGATE_PREVIOUS");
+ private static final Field FIELD_IME_ACTION_PREVIOUS = CompatUtils.getField(
+ EditorInfo.class, "IME_FLAG_ACTION_PREVIOUS");
+ private static final Integer OBJ_IME_FLAG_NAVIGATE_NEXT = (Integer) CompatUtils
+ .getFieldValue(null, null, FIELD_IME_FLAG_NAVIGATE_NEXT);
+ private static final Integer OBJ_IME_FLAG_NAVIGATE_PREVIOUS = (Integer) CompatUtils
+ .getFieldValue(null, null, FIELD_IME_FLAG_NAVIGATE_PREVIOUS);
+ private static final Integer OBJ_IME_ACTION_PREVIOUS = (Integer) CompatUtils
+ .getFieldValue(null, null, FIELD_IME_ACTION_PREVIOUS);
+
+ public static boolean hasFlagNavigateNext(int imeOptions) {
+ if (OBJ_IME_FLAG_NAVIGATE_NEXT == null)
+ return false;
+ return (imeOptions & OBJ_IME_FLAG_NAVIGATE_NEXT) != 0;
+ }
+
+ public static boolean hasFlagNavigatePrevious(int imeOptions) {
+ if (OBJ_IME_FLAG_NAVIGATE_PREVIOUS == null)
+ return false;
+ return (imeOptions & OBJ_IME_FLAG_NAVIGATE_PREVIOUS) != 0;
+ }
+
+ public static void performEditorActionNext(InputConnection ic) {
+ ic.performEditorAction(EditorInfo.IME_ACTION_NEXT);
+ }
+
+ public static void performEditorActionPrevious(InputConnection ic) {
+ if (OBJ_IME_ACTION_PREVIOUS == null)
+ return;
+ ic.performEditorAction(OBJ_IME_ACTION_PREVIOUS);
+ }
+
+ public static String imeOptionsName(int imeOptions) {
+ if (imeOptions == -1)
+ return null;
+ final int actionId = imeOptions & EditorInfo.IME_MASK_ACTION;
+ final String action;
+ switch (actionId) {
+ case EditorInfo.IME_ACTION_UNSPECIFIED:
+ action = "actionUnspecified";
+ break;
+ case EditorInfo.IME_ACTION_NONE:
+ action = "actionNone";
+ break;
+ case EditorInfo.IME_ACTION_GO:
+ action = "actionGo";
+ break;
+ case EditorInfo.IME_ACTION_SEARCH:
+ action = "actionSearch";
+ break;
+ case EditorInfo.IME_ACTION_SEND:
+ action = "actionSend";
+ break;
+ case EditorInfo.IME_ACTION_NEXT:
+ action = "actionNext";
+ break;
+ case EditorInfo.IME_ACTION_DONE:
+ action = "actionDone";
+ break;
+ default: {
+ if (OBJ_IME_ACTION_PREVIOUS != null && actionId == OBJ_IME_ACTION_PREVIOUS) {
+ action = "actionPrevious";
+ } else {
+ action = "actionUnknown(" + actionId + ")";
+ }
+ break;
+ }
+ }
+ if ((imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
+ return "flagNoEnterAction|" + action;
+ } else {
+ return action;
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java
new file mode 100644
index 000000000..7d00b6007
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import com.android.inputmethod.latin.EditingUtils.SelectedWord;
+
+import android.view.inputmethod.InputConnection;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+public class InputConnectionCompatUtils {
+ private static final Class<?> CLASS_CorrectionInfo = CompatUtils
+ .getClass("android.view.inputmethod.CorrectionInfo");
+ private static final Class<?>[] INPUT_TYPE_CorrectionInfo = new Class<?>[] { int.class,
+ CharSequence.class, CharSequence.class };
+ private static final Constructor<?> CONSTRUCTOR_CorrectionInfo = CompatUtils
+ .getConstructor(CLASS_CorrectionInfo, INPUT_TYPE_CorrectionInfo);
+ private static final Method METHOD_InputConnection_commitCorrection = CompatUtils
+ .getMethod(InputConnection.class, "commitCorrection", CLASS_CorrectionInfo);
+ private static final Method METHOD_getSelectedText = CompatUtils
+ .getMethod(InputConnection.class, "getSelectedText", int.class);
+ private static final Method METHOD_setComposingRegion = CompatUtils
+ .getMethod(InputConnection.class, "setComposingRegion", int.class, int.class);
+ public static final boolean RECORRECTION_SUPPORTED;
+
+ static {
+ RECORRECTION_SUPPORTED = METHOD_getSelectedText != null
+ && METHOD_setComposingRegion != null;
+ }
+
+ public static void commitCorrection(InputConnection ic, int offset, CharSequence oldText,
+ CharSequence newText) {
+ if (ic == null || CONSTRUCTOR_CorrectionInfo == null
+ || METHOD_InputConnection_commitCorrection == null) {
+ return;
+ }
+ Object[] args = { offset, oldText, newText };
+ Object correctionInfo = CompatUtils.newInstance(CONSTRUCTOR_CorrectionInfo, args);
+ if (correctionInfo != null) {
+ CompatUtils.invoke(ic, null, METHOD_InputConnection_commitCorrection,
+ correctionInfo);
+ }
+ }
+
+
+ /**
+ * Returns the selected text between the selStart and selEnd positions.
+ */
+ public static CharSequence getSelectedText(InputConnection ic, int selStart, int selEnd) {
+ // Use reflection, for backward compatibility
+ return (CharSequence) CompatUtils.invoke(
+ ic, null, METHOD_getSelectedText, 0);
+ }
+
+ /**
+ * Tries to set the text into composition mode if there is support for it in the framework.
+ */
+ public static void underlineWord(InputConnection ic, SelectedWord word) {
+ // Use reflection, for backward compatibility
+ // If method not found, there's nothing we can do. It still works but just wont underline
+ // the word.
+ CompatUtils.invoke(
+ ic, null, METHOD_setComposingRegion, word.mStart, word.mEnd);
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
new file mode 100644
index 000000000..8e22bbc79
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import android.content.pm.ServiceInfo;
+import android.view.inputmethod.InputMethodInfo;
+
+import java.lang.reflect.Method;
+
+public class InputMethodInfoCompatWrapper {
+ private final InputMethodInfo mImi;
+ private static final Method METHOD_getSubtypeAt = CompatUtils.getMethod(
+ InputMethodInfo.class, "getSubtypeAt", int.class);
+ private static final Method METHOD_getSubtypeCount = CompatUtils.getMethod(
+ InputMethodInfo.class, "getSubtypeCount");
+
+ public InputMethodInfoCompatWrapper(InputMethodInfo imi) {
+ mImi = imi;
+ }
+
+ public InputMethodInfo getInputMethodInfo() {
+ return mImi;
+ }
+
+ public String getId() {
+ return mImi.getId();
+ }
+
+ public String getPackageName() {
+ return mImi.getPackageName();
+ }
+
+ public ServiceInfo getServiceInfo() {
+ return mImi.getServiceInfo();
+ }
+
+ public int getSubtypeCount() {
+ return (Integer) CompatUtils.invoke(mImi, 0, METHOD_getSubtypeCount);
+ }
+
+ public InputMethodSubtypeCompatWrapper getSubtypeAt(int index) {
+ return new InputMethodSubtypeCompatWrapper(CompatUtils.invoke(mImi, null,
+ METHOD_getSubtypeAt, index));
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
new file mode 100644
index 000000000..1cc13f249
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+// TODO: Override this class with the concrete implementation if we need to take care of the
+// performance.
+public class InputMethodManagerCompatWrapper {
+ private static final String TAG = InputMethodManagerCompatWrapper.class.getSimpleName();
+ private static final Method METHOD_getCurrentInputMethodSubtype =
+ CompatUtils.getMethod(InputMethodManager.class, "getCurrentInputMethodSubtype");
+ private static final Method METHOD_getEnabledInputMethodSubtypeList =
+ CompatUtils.getMethod(InputMethodManager.class, "getEnabledInputMethodSubtypeList",
+ InputMethodInfo.class, boolean.class);
+ private static final Method METHOD_getShortcutInputMethodsAndSubtypes =
+ CompatUtils.getMethod(InputMethodManager.class, "getShortcutInputMethodsAndSubtypes");
+ private static final Method METHOD_setInputMethodAndSubtype =
+ CompatUtils.getMethod(
+ InputMethodManager.class, "setInputMethodAndSubtype", IBinder.class,
+ String.class, InputMethodSubtypeCompatWrapper.CLASS_InputMethodSubtype);
+ private static final Method METHOD_switchToLastInputMethod = CompatUtils.getMethod(
+ InputMethodManager.class, "switchToLastInputMethod", IBinder.class);
+
+ private static final InputMethodManagerCompatWrapper sInstance =
+ new InputMethodManagerCompatWrapper();
+
+ public static final boolean SUBTYPE_SUPPORTED;
+
+ static {
+ // This static initializer guarantees that METHOD_getShortcutInputMethodsAndSubtypes is
+ // already instantiated.
+ SUBTYPE_SUPPORTED = METHOD_getShortcutInputMethodsAndSubtypes != null;
+ }
+
+ // For the compatibility, IMM will create dummy subtypes if subtypes are not found.
+ // This is required to be false if the current behavior is broken. For now, it's ok to be true.
+ public static final boolean FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES =
+ !InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED;
+ private static final String VOICE_MODE = "voice";
+ private static final String KEYBOARD_MODE = "keyboard";
+
+ private InputMethodManager mImm;
+ private LanguageSwitcherProxy mLanguageSwitcherProxy;
+ private String mLatinImePackageName;
+
+ private InputMethodManagerCompatWrapper() {
+ }
+
+ public static InputMethodManagerCompatWrapper getInstance(Context context) {
+ if (sInstance.mImm == null) {
+ sInstance.init(context);
+ }
+ return sInstance;
+ }
+
+ private synchronized void init(Context context) {
+ mImm = (InputMethodManager) context.getSystemService(
+ Context.INPUT_METHOD_SERVICE);
+ if (context instanceof LatinIME) {
+ mLatinImePackageName = context.getPackageName();
+ }
+ mLanguageSwitcherProxy = LanguageSwitcherProxy.getInstance();
+ }
+
+ public InputMethodSubtypeCompatWrapper getCurrentInputMethodSubtype() {
+ if (!SUBTYPE_SUPPORTED) {
+ return new InputMethodSubtypeCompatWrapper(
+ 0, 0, mLanguageSwitcherProxy.getInputLocale().toString(), KEYBOARD_MODE, "");
+ }
+ Object o = CompatUtils.invoke(mImm, null, METHOD_getCurrentInputMethodSubtype);
+ return new InputMethodSubtypeCompatWrapper(o);
+ }
+
+ public List<InputMethodSubtypeCompatWrapper> getEnabledInputMethodSubtypeList(
+ InputMethodInfoCompatWrapper imi, boolean allowsImplicitlySelectedSubtypes) {
+ if (!SUBTYPE_SUPPORTED) {
+ String[] languages = mLanguageSwitcherProxy.getEnabledLanguages(
+ allowsImplicitlySelectedSubtypes);
+ List<InputMethodSubtypeCompatWrapper> subtypeList =
+ new ArrayList<InputMethodSubtypeCompatWrapper>();
+ for (String lang: languages) {
+ subtypeList.add(new InputMethodSubtypeCompatWrapper(0, 0, lang, KEYBOARD_MODE, ""));
+ }
+ return subtypeList;
+ }
+ Object retval = CompatUtils.invoke(mImm, null, METHOD_getEnabledInputMethodSubtypeList,
+ (imi != null ? imi.getInputMethodInfo() : null), allowsImplicitlySelectedSubtypes);
+ if (retval == null || !(retval instanceof List<?>) || ((List<?>)retval).isEmpty()) {
+ if (!FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES) {
+ // Returns an empty list
+ return Collections.emptyList();
+ }
+ // Creates dummy subtypes
+ @SuppressWarnings("unused")
+ List<InputMethodSubtypeCompatWrapper> subtypeList =
+ new ArrayList<InputMethodSubtypeCompatWrapper>();
+ InputMethodSubtypeCompatWrapper keyboardSubtype = getLastResortSubtype(KEYBOARD_MODE);
+ InputMethodSubtypeCompatWrapper voiceSubtype = getLastResortSubtype(VOICE_MODE);
+ if (keyboardSubtype != null) {
+ subtypeList.add(keyboardSubtype);
+ }
+ if (voiceSubtype != null) {
+ subtypeList.add(voiceSubtype);
+ }
+ return subtypeList;
+ }
+ return CompatUtils.copyInputMethodSubtypeListToWrapper(retval);
+ }
+
+ private InputMethodInfoCompatWrapper getLatinImeInputMethodInfo() {
+ if (TextUtils.isEmpty(mLatinImePackageName))
+ return null;
+ return Utils.getInputMethodInfo(this, mLatinImePackageName);
+ }
+
+ @SuppressWarnings("unused")
+ private InputMethodSubtypeCompatWrapper getLastResortSubtype(String mode) {
+ if (VOICE_MODE.equals(mode) && !FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES)
+ return null;
+ Locale inputLocale = SubtypeSwitcher.getInstance().getInputLocale();
+ if (inputLocale == null)
+ return null;
+ return new InputMethodSubtypeCompatWrapper(0, 0, inputLocale.toString(), mode, "");
+ }
+
+ public Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>
+ getShortcutInputMethodsAndSubtypes() {
+ Object retval = CompatUtils.invoke(mImm, null, METHOD_getShortcutInputMethodsAndSubtypes);
+ if (retval == null || !(retval instanceof Map<?, ?>) || ((Map<?, ?>)retval).isEmpty()) {
+ if (!FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES) {
+ // Returns an empty map
+ return Collections.emptyMap();
+ }
+ // Creates dummy subtypes
+ @SuppressWarnings("unused")
+ InputMethodInfoCompatWrapper imi = getLatinImeInputMethodInfo();
+ InputMethodSubtypeCompatWrapper voiceSubtype = getLastResortSubtype(VOICE_MODE);
+ if (imi != null && voiceSubtype != null) {
+ Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>
+ shortcutMap =
+ new HashMap<InputMethodInfoCompatWrapper,
+ List<InputMethodSubtypeCompatWrapper>>();
+ List<InputMethodSubtypeCompatWrapper> subtypeList =
+ new ArrayList<InputMethodSubtypeCompatWrapper>();
+ subtypeList.add(voiceSubtype);
+ shortcutMap.put(imi, subtypeList);
+ return shortcutMap;
+ } else {
+ return Collections.emptyMap();
+ }
+ }
+ Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>> shortcutMap =
+ new HashMap<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>>();
+ final Map<?, ?> retvalMap = (Map<?, ?>)retval;
+ for (Object key : retvalMap.keySet()) {
+ if (!(key instanceof InputMethodInfo)) {
+ Log.e(TAG, "Class type error.");
+ return null;
+ }
+ shortcutMap.put(new InputMethodInfoCompatWrapper((InputMethodInfo)key),
+ CompatUtils.copyInputMethodSubtypeListToWrapper(retvalMap.get(key)));
+ }
+ return shortcutMap;
+ }
+
+ public void setInputMethodAndSubtype(
+ IBinder token, String id, InputMethodSubtypeCompatWrapper subtype) {
+ if (subtype != null && subtype.hasOriginalObject()) {
+ CompatUtils.invoke(mImm, null, METHOD_setInputMethodAndSubtype,
+ token, id, subtype.getOriginalObject());
+ }
+ }
+
+ public boolean switchToLastInputMethod(IBinder token) {
+ if (SubtypeSwitcher.getInstance().isDummyVoiceMode()) {
+ return true;
+ }
+ return (Boolean)CompatUtils.invoke(mImm, false, METHOD_switchToLastInputMethod, token);
+ }
+
+ public List<InputMethodInfoCompatWrapper> getEnabledInputMethodList() {
+ if (mImm == null) return null;
+ List<InputMethodInfoCompatWrapper> imis = new ArrayList<InputMethodInfoCompatWrapper>();
+ for (InputMethodInfo imi : mImm.getEnabledInputMethodList()) {
+ imis.add(new InputMethodInfoCompatWrapper(imi));
+ }
+ return imis;
+ }
+
+ public void showInputMethodPicker() {
+ if (mImm == null) return;
+ mImm.showInputMethodPicker();
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
new file mode 100644
index 000000000..828aea41f
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+
+import android.inputmethodservice.InputMethodService;
+import android.view.inputmethod.InputMethodSubtype;
+
+public class InputMethodServiceCompatWrapper extends InputMethodService {
+ // CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED needs to be false if the API level is 10
+ // or previous. Note that InputMethodSubtype was added in the API level 11.
+ // For the API level 11 or later, LatinIME should override onCurrentInputMethodSubtypeChanged().
+ // For the API level 10 or previous, we handle the "subtype changed" events by ourselves
+ // without having support from framework -- onCurrentInputMethodSubtypeChanged().
+ public static final boolean CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED = true;
+
+ private InputMethodManagerCompatWrapper mImm;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mImm = InputMethodManagerCompatWrapper.getInstance(this);
+ }
+
+ // When the API level is 10 or previous, notifyOnCurrentInputMethodSubtypeChanged should
+ // handle the event the current subtype was changed. LatinIME calls
+ // notifyOnCurrentInputMethodSubtypeChanged every time LatinIME
+ // changes the current subtype.
+ // This call is required to let LatinIME itself know a subtype changed
+ // event when the API level is 10 or previous.
+ @SuppressWarnings("unused")
+ public void notifyOnCurrentInputMethodSubtypeChanged(InputMethodSubtypeCompatWrapper subtype) {
+ // Do nothing when the API level is 11 or later
+ // and FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES is not true
+ if (CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED && !InputMethodManagerCompatWrapper.
+ FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES) {
+ return;
+ }
+ if (subtype == null) {
+ subtype = mImm.getCurrentInputMethodSubtype();
+ }
+ if (subtype != null) {
+ if (!InputMethodManagerCompatWrapper.FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES
+ && !subtype.isDummy()) return;
+ if (!InputMethodManagerCompatWrapper.SUBTYPE_SUPPORTED) {
+ LanguageSwitcherProxy.getInstance().setLocale(subtype.getLocale());
+ }
+ SubtypeSwitcher.getInstance().updateSubtype(subtype);
+ }
+ }
+
+ //////////////////////////////////////
+ // Functions using API v11 or later //
+ //////////////////////////////////////
+ @Override
+ public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
+ // Do nothing when the API level is 10 or previous
+ if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) return;
+ SubtypeSwitcher.getInstance().updateSubtype(
+ new InputMethodSubtypeCompatWrapper(subtype));
+ }
+
+ protected static void setTouchableRegionCompat(InputMethodService.Insets outInsets,
+ int x, int y, int width, int height) {
+ outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
+ outInsets.touchableRegion.set(x, y, width, height);
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
new file mode 100644
index 000000000..806c355a9
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import com.android.inputmethod.latin.LatinImeLogger;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+// TODO: Override this class with the concrete implementation if we need to take care of the
+// performance.
+public final class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper {
+ private static final boolean DBG = LatinImeLogger.sDBG;
+ private static final String TAG = InputMethodSubtypeCompatWrapper.class.getSimpleName();
+ private static final String DEFAULT_LOCALE = "en_US";
+ private static final String DEFAULT_MODE = "keyboard";
+
+ public static final Class<?> CLASS_InputMethodSubtype =
+ CompatUtils.getClass("android.view.inputmethod.InputMethodSubtype");
+ private static final Method METHOD_getNameResId =
+ CompatUtils.getMethod(CLASS_InputMethodSubtype, "getNameResId");
+ private static final Method METHOD_getIconResId =
+ CompatUtils.getMethod(CLASS_InputMethodSubtype, "getIconResId");
+ private static final Method METHOD_getLocale =
+ CompatUtils.getMethod(CLASS_InputMethodSubtype, "getLocale");
+ private static final Method METHOD_getMode =
+ CompatUtils.getMethod(CLASS_InputMethodSubtype, "getMode");
+ private static final Method METHOD_getExtraValue =
+ CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValue");
+ private static final Method METHOD_containsExtraValueKey =
+ CompatUtils.getMethod(CLASS_InputMethodSubtype, "containsExtraValueKey", String.class);
+ private static final Method METHOD_getExtraValueOf =
+ CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValueOf", String.class);
+
+ private final int mDummyNameResId;
+ private final int mDummyIconResId;
+ private final String mDummyLocale;
+ private final String mDummyMode;
+ private final String mDummyExtraValues;
+
+ public InputMethodSubtypeCompatWrapper(Object subtype) {
+ super((CLASS_InputMethodSubtype != null && CLASS_InputMethodSubtype.isInstance(subtype))
+ ? subtype : null);
+ mDummyNameResId = 0;
+ mDummyIconResId = 0;
+ mDummyLocale = DEFAULT_LOCALE;
+ mDummyMode = DEFAULT_MODE;
+ mDummyExtraValues = "";
+ }
+
+ // Constructor for creating a dummy subtype.
+ public InputMethodSubtypeCompatWrapper(int nameResId, int iconResId, String locale,
+ String mode, String extraValues) {
+ super(null);
+ if (DBG) {
+ Log.d(TAG, "CreateInputMethodSubtypeCompatWrapper");
+ }
+ mDummyNameResId = nameResId;
+ mDummyIconResId = iconResId;
+ mDummyLocale = locale != null ? locale : "";
+ mDummyMode = mode != null ? mode : "";
+ mDummyExtraValues = extraValues != null ? extraValues : "";
+ }
+
+ public int getNameResId() {
+ if (mObj == null) return mDummyNameResId;
+ return (Integer)CompatUtils.invoke(mObj, 0, METHOD_getNameResId);
+ }
+
+ public int getIconResId() {
+ if (mObj == null) return mDummyIconResId;
+ return (Integer)CompatUtils.invoke(mObj, 0, METHOD_getIconResId);
+ }
+
+ public String getLocale() {
+ if (mObj == null) return mDummyLocale;
+ final String s = (String)CompatUtils.invoke(mObj, null, METHOD_getLocale);
+ if (TextUtils.isEmpty(s)) return DEFAULT_LOCALE;
+ return s;
+ }
+
+ public String getMode() {
+ if (mObj == null) return mDummyMode;
+ String s = (String)CompatUtils.invoke(mObj, null, METHOD_getMode);
+ if (TextUtils.isEmpty(s)) return DEFAULT_MODE;
+ return s;
+ }
+
+ public String getExtraValue() {
+ if (mObj == null) return mDummyExtraValues;
+ return (String)CompatUtils.invoke(mObj, null, METHOD_getExtraValue);
+ }
+
+ public boolean containsExtraValueKey(String key) {
+ return (Boolean)CompatUtils.invoke(mObj, false, METHOD_containsExtraValueKey, key);
+ }
+
+ public String getExtraValueOf(String key) {
+ return (String)CompatUtils.invoke(mObj, null, METHOD_getExtraValueOf, key);
+ }
+
+ public boolean isDummy() {
+ return !hasOriginalObject();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof InputMethodSubtypeCompatWrapper) {
+ InputMethodSubtypeCompatWrapper subtype = (InputMethodSubtypeCompatWrapper)o;
+ if (mObj == null) {
+ // easy check of dummy subtypes
+ return (mDummyNameResId == subtype.mDummyNameResId
+ && mDummyIconResId == subtype.mDummyIconResId
+ && mDummyLocale.equals(subtype.mDummyLocale)
+ && mDummyMode.equals(subtype.mDummyMode)
+ && mDummyExtraValues.equals(subtype.mDummyExtraValues));
+ }
+ return mObj.equals(subtype.getOriginalObject());
+ } else {
+ return mObj.equals(o);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ if (mObj == null) {
+ return hashCodeInternal(mDummyNameResId, mDummyIconResId, mDummyLocale,
+ mDummyMode, mDummyExtraValues);
+ }
+ return mObj.hashCode();
+ }
+
+ private static int hashCodeInternal(int nameResId, int iconResId, String locale,
+ String mode, String extraValue) {
+ return Arrays
+ .hashCode(new Object[] { nameResId, iconResId, locale, mode, extraValue });
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java
new file mode 100644
index 000000000..6c2f0f799
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import android.text.InputType;
+
+import java.lang.reflect.Field;
+
+public class InputTypeCompatUtils {
+ private static final Field FIELD_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS =
+ CompatUtils.getField(InputType.class, "TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS");
+ private static final Field FIELD_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD = CompatUtils
+ .getField(InputType.class, "TYPE_TEXT_VARIATION_WEB_PASSWORD");
+ private static final Field FIELD_InputType_TYPE_NUMBER_VARIATION_PASSWORD = CompatUtils
+ .getField(InputType.class, "TYPE_NUMBER_VARIATION_PASSWORD");
+ private static final Integer OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS =
+ (Integer) CompatUtils.getFieldValue(null, null,
+ FIELD_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS);
+ private static final Integer OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD =
+ (Integer) CompatUtils.getFieldValue(null, null,
+ FIELD_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD);
+ private static final Integer OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD =
+ (Integer) CompatUtils.getFieldValue(null, null,
+ FIELD_InputType_TYPE_NUMBER_VARIATION_PASSWORD);
+ private static final int WEB_TEXT_PASSWORD_INPUT_TYPE;
+ private static final int WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE;
+ private static final int NUMBER_PASSWORD_INPUT_TYPE;
+ private static final int TEXT_PASSWORD_INPUT_TYPE =
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD;
+ private static final int TEXT_VISIBLE_PASSWORD_INPUT_TYPE =
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
+
+ static {
+ WEB_TEXT_PASSWORD_INPUT_TYPE =
+ OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD != null
+ ? InputType.TYPE_CLASS_TEXT | OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD
+ : 0;
+ WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE =
+ OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != null
+ ? InputType.TYPE_CLASS_TEXT
+ | OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS
+ : 0;
+ NUMBER_PASSWORD_INPUT_TYPE =
+ OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD != null
+ ? InputType.TYPE_CLASS_NUMBER | OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD
+ : 0;
+ }
+
+ private static boolean isWebEditTextInputType(int inputType) {
+ return inputType == (InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
+ }
+
+ private static boolean isWebPasswordInputType(int inputType) {
+ return WEB_TEXT_PASSWORD_INPUT_TYPE != 0
+ && inputType == WEB_TEXT_PASSWORD_INPUT_TYPE;
+ }
+
+ private static boolean isWebEmailAddressInputType(int inputType) {
+ return WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE != 0
+ && inputType == WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE;
+ }
+
+ private static boolean isNumberPasswordInputType(int inputType) {
+ return NUMBER_PASSWORD_INPUT_TYPE != 0
+ && inputType == NUMBER_PASSWORD_INPUT_TYPE;
+ }
+
+ private static boolean isTextPasswordInputType(int inputType) {
+ return inputType == TEXT_PASSWORD_INPUT_TYPE;
+ }
+
+ private static boolean isWebEmailAddressVariation(int variation) {
+ return OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != null
+ && variation == OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
+ }
+
+ public static boolean isEmailVariation(int variation) {
+ return variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+ || isWebEmailAddressVariation(variation);
+ }
+
+ public static boolean isWebInputType(int inputType) {
+ final int maskedInputType =
+ inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
+ return isWebEditTextInputType(maskedInputType) || isWebPasswordInputType(maskedInputType)
+ || isWebEmailAddressInputType(maskedInputType);
+ }
+
+ // Please refer to TextView.isPasswordInputType
+ public static boolean isPasswordInputType(int inputType) {
+ final int maskedInputType =
+ inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
+ return isTextPasswordInputType(maskedInputType) || isWebPasswordInputType(maskedInputType)
+ || isNumberPasswordInputType(maskedInputType);
+ }
+
+ // Please refer to TextView.isVisiblePasswordInputType
+ public static boolean isVisiblePasswordInputType(int inputType) {
+ final int maskedInputType =
+ inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
+ return maskedInputType == TEXT_VISIBLE_PASSWORD_INPUT_TYPE;
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
new file mode 100644
index 000000000..5b02de36e
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver;
+
+import android.content.Context;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+
+import java.lang.reflect.Constructor;
+import java.util.Locale;
+
+public class SuggestionSpanUtils {
+ public static final String ACTION_SUGGESTION_PICKED =
+ "android.text.style.SUGGESTION_PICKED";
+ public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
+ public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before";
+ public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode";
+
+ private static final Class<?> CLASS_SuggestionSpan = CompatUtils
+ .getClass("android.text.style.SuggestionSpan");
+ private static final Class<?>[] INPUT_TYPE_SuggestionSpan = new Class<?>[] {
+ Context.class, Locale.class, String[].class, int.class, Class.class };
+ private static final Constructor<?> CONSTRUCTOR_SuggestionSpan = CompatUtils
+ .getConstructor(CLASS_SuggestionSpan, INPUT_TYPE_SuggestionSpan);
+ public static final boolean SUGGESTION_SPAN_IS_SUPPORTED;
+ static {
+ SUGGESTION_SPAN_IS_SUPPORTED =
+ CLASS_SuggestionSpan != null && CONSTRUCTOR_SuggestionSpan != null;
+ }
+
+ public static CharSequence getTextWithSuggestionSpan(Context context,
+ CharSequence suggestion, SuggestedWords suggestedWords) {
+ if (CONSTRUCTOR_SuggestionSpan == null || suggestedWords == null
+ || suggestedWords.size() == 0) {
+ return suggestion;
+ }
+
+ final Spannable spannable;
+ if (suggestion instanceof Spannable) {
+ spannable = (Spannable) suggestion;
+ } else {
+ spannable = new SpannableString(suggestion);
+ }
+ // TODO: Use SUGGESTIONS_MAX_SIZE instead of 5.
+ final int N = Math.min(5, suggestedWords.size());
+ final String[] suggestionsArray = new String[N];
+ for (int i = 0; i < N; ++i) {
+ suggestionsArray[i] = suggestedWords.getWord(i).toString();
+ }
+ final Object[] args =
+ { context, null, suggestionsArray, 0,
+ (Class<?>) SuggestionSpanPickedNotificationReceiver.class };
+ final Object ss = CompatUtils.newInstance(CONSTRUCTOR_SuggestionSpan, args);
+ if (ss == null) {
+ return suggestion;
+ }
+ spannable.setSpan(ss, 0, suggestion.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return spannable;
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
new file mode 100644
index 000000000..8e2a2e0b8
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import android.content.Context;
+import android.os.Vibrator;
+
+import java.lang.reflect.Method;
+
+public class VibratorCompatWrapper {
+ private static final Method METHOD_hasVibrator = CompatUtils.getMethod(Vibrator.class,
+ "hasVibrator", int.class);
+
+ private static final VibratorCompatWrapper sInstance = new VibratorCompatWrapper();
+ private Vibrator mVibrator;
+
+ private VibratorCompatWrapper() {
+ }
+
+ public static VibratorCompatWrapper getInstance(Context context) {
+ if (sInstance.mVibrator == null) {
+ sInstance.mVibrator =
+ (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ }
+ return sInstance;
+ }
+
+ public boolean hasVibrator() {
+ if (mVibrator == null)
+ return false;
+ return (Boolean) CompatUtils.invoke(mVibrator, true, METHOD_hasVibrator);
+ }
+}