aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java5
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java4
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java15
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java2
-rw-r--r--java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java17
-rw-r--r--java/src/com/android/inputmethod/compat/AccessibilityManagerCompatWrapper.java14
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java18
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java130
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java30
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java32
-rw-r--r--java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java2
-rw-r--r--java/src/com/android/inputmethod/deprecated/VoiceProxy.java190
-rw-r--r--java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java4
-rw-r--r--java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java216
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyDetector.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java326
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardId.java62
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java456
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java205
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboard.java175
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java43
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java83
-rw-r--r--java/src/com/android/inputmethod/keyboard/MiniKeyboard.java24
-rw-r--r--java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java163
-rw-r--r--java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java28
-rw-r--r--java/src/com/android/inputmethod/keyboard/PopupPanel.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/ProximityInfo.java15
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java (renamed from java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java)278
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java61
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java95
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java120
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java9
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java52
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/Row.java42
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java158
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java15
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java162
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java50
-rw-r--r--java/src/com/android/inputmethod/latin/CandidateView.java740
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsDictionary.java16
-rw-r--r--java/src/com/android/inputmethod/latin/DebugSettings.java8
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java18
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java18
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFactory.java48
-rw-r--r--java/src/com/android/inputmethod/latin/EditingUtils.java10
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java446
-rw-r--r--java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java29
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java133
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java151
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java76
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java19
-rw-r--r--java/src/com/android/inputmethod/latin/UserDictionary.java29
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java60
-rw-r--r--java/src/com/android/inputmethod/latin/WhitelistDictionary.java5
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java167
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java115
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java94
62 files changed, 2925 insertions, 2591 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
index ae614b7e0..7e71b5f36 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -16,7 +16,6 @@
package com.android.inputmethod.accessibility;
-import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.content.SharedPreferences;
import android.inputmethodservice.InputMethodService;
@@ -82,10 +81,8 @@ public class AccessibilityUtils {
*/
public boolean isTouchExplorationEnabled() {
return ENABLE_ACCESSIBILITY
- && AccessibilityEventCompatUtils.supportsTouchExploration()
&& mAccessibilityManager.isEnabled()
- && !mCompatManager.getEnabledAccessibilityServiceList(
- AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty();
+ && mCompatManager.isTouchExplorationEnabled();
}
/**
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
index 89adc15f2..4ab9cb898 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
@@ -47,6 +47,8 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi
*/
private static final long VIBRATE_KEY_CLICK = 50;
+ private static final float FX_VOLUME = -1.0f;
+
private InputMethodService mInputMethod;
private Vibrator mVibrator;
private AudioManager mAudioManager;
@@ -143,7 +145,7 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi
*/
private void sendDownUpKeyEvents(int keyCode) {
mVibrator.vibrate(VIBRATE_KEY_CLICK);
- mAudioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
+ mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, FX_VOLUME);
mInputMethod.sendDownUpKeyEvents(keyCode);
}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index 8ca834148..ae9809e56 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -105,28 +105,19 @@ public class AccessibleKeyboardViewProxy {
}
/**
- * Receives hover events when accessibility is turned on in API > 11. In
- * earlier API levels, events are manually routed from onTouchEvent.
+ * Receives hover events when accessibility is turned on in SDK versions ICS
+ * and higher.
*
* @param event The hover event.
* @return {@code true} if the event is handled
*/
- public boolean onHoverEvent(MotionEvent event, PointerTracker tracker) {
+ public boolean dispatchHoverEvent(MotionEvent event, PointerTracker tracker) {
if (mGestureDetector.onHoverEvent(event, this, tracker))
return true;
return onHoverEventInternal(event, tracker);
}
- public boolean dispatchTouchEvent(MotionEvent event) {
- // Since touch exploration translates hover double-tap to a regular
- // single-tap, we're going to drop non-touch exploration events.
- if (!AccessibilityUtils.getInstance().isTouchExplorationEvent(event))
- return true;
-
- return false;
- }
-
/**
* Handles touch exploration events when Accessibility is turned on.
*
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index d196c8955..ec4287dda 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -164,7 +164,7 @@ public class KeyCodeDescriptionMapper {
return context.getString(R.string.spoken_description_to_symbol);
} else if (id.isSymbolsKeyboard()) {
return context.getString(R.string.spoken_description_to_alpha);
- } else if (id.isPhoneSymbolsKeyboard()) {
+ } else if (id.isPhoneShiftKeyboard()) {
return context.getString(R.string.spoken_description_to_numeric);
} else if (id.isPhoneKeyboard()) {
return context.getString(R.string.spoken_description_to_symbol);
diff --git a/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java b/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java
index 50057727a..2fa9d87d8 100644
--- a/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/AccessibilityEventCompatUtils.java
@@ -16,24 +16,7 @@
package com.android.inputmethod.compat;
-import android.view.accessibility.AccessibilityEvent;
-
-import java.lang.reflect.Field;
-
public class AccessibilityEventCompatUtils {
public static final int TYPE_VIEW_HOVER_ENTER = 0x80;
public static final int TYPE_VIEW_HOVER_EXIT = 0x100;
-
- private static final Field FIELD_TYPE_VIEW_HOVER_ENTER = CompatUtils.getField(
- AccessibilityEvent.class, "TYPE_VIEW_HOVER_ENTER");
- private static final Field FIELD_TYPE_VIEW_HOVER_EXIT = CompatUtils.getField(
- AccessibilityEvent.class, "TYPE_VIEW_HOVER_EXIT");
- private static final Integer OBJ_TYPE_VIEW_HOVER_ENTER = (Integer) CompatUtils
- .getFieldValue(null, null, FIELD_TYPE_VIEW_HOVER_ENTER);
- private static final Integer OBJ_TYPE_VIEW_HOVER_EXIT = (Integer) CompatUtils
- .getFieldValue(null, null, FIELD_TYPE_VIEW_HOVER_EXIT);
-
- public static boolean supportsTouchExploration() {
- return OBJ_TYPE_VIEW_HOVER_ENTER != null && OBJ_TYPE_VIEW_HOVER_EXIT != null;
- }
}
diff --git a/java/src/com/android/inputmethod/compat/AccessibilityManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/AccessibilityManagerCompatWrapper.java
index 4db1c7a24..a30af0faf 100644
--- a/java/src/com/android/inputmethod/compat/AccessibilityManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/AccessibilityManagerCompatWrapper.java
@@ -16,16 +16,13 @@
package com.android.inputmethod.compat;
-import android.accessibilityservice.AccessibilityServiceInfo;
import android.view.accessibility.AccessibilityManager;
import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.List;
public class AccessibilityManagerCompatWrapper {
- private static final Method METHOD_getEnabledAccessibilityServiceList = CompatUtils.getMethod(
- AccessibilityManager.class, "getEnabledAccessibilityServiceList", int.class);
+ private static final Method METHOD_isTouchExplorationEnabled = CompatUtils.getMethod(
+ AccessibilityManager.class, "isTouchExplorationEnabled");
private final AccessibilityManager mManager;
@@ -33,10 +30,7 @@ public class AccessibilityManagerCompatWrapper {
mManager = manager;
}
- @SuppressWarnings("unchecked")
- public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
- return (List<AccessibilityServiceInfo>) CompatUtils.invoke(mManager,
- Collections.<AccessibilityServiceInfo>emptyList(),
- METHOD_getEnabledAccessibilityServiceList, feedbackType);
+ public boolean isTouchExplorationEnabled() {
+ return (Boolean) CompatUtils.invoke(mManager, false, METHOD_isTouchExplorationEnabled);
}
}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
index 8e22bbc79..831559809 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodInfoCompatWrapper.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.compat;
+import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.view.inputmethod.InputMethodInfo;
@@ -56,4 +57,21 @@ public class InputMethodInfoCompatWrapper {
return new InputMethodSubtypeCompatWrapper(CompatUtils.invoke(mImi, null,
METHOD_getSubtypeAt, index));
}
+
+ public CharSequence loadLabel(PackageManager pm) {
+ return mImi.loadLabel(pm);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof InputMethodInfoCompatWrapper) {
+ return mImi.equals(((InputMethodInfoCompatWrapper)o).mImi);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mImi.hashCode();
+ }
}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index 1cc13f249..51dc4cd37 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -16,21 +16,28 @@
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.app.AlertDialog;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
+import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
+
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -72,27 +79,27 @@ public class InputMethodManagerCompatWrapper {
private static final String VOICE_MODE = "voice";
private static final String KEYBOARD_MODE = "keyboard";
+ private InputMethodServiceCompatWrapper mService;
private InputMethodManager mImm;
+ private PackageManager mPackageManager;
+ private ApplicationInfo mApplicationInfo;
private LanguageSwitcherProxy mLanguageSwitcherProxy;
private String mLatinImePackageName;
- private InputMethodManagerCompatWrapper() {
- }
-
- public static InputMethodManagerCompatWrapper getInstance(Context context) {
- if (sInstance.mImm == null) {
- sInstance.init(context);
- }
+ public static InputMethodManagerCompatWrapper getInstance() {
+ if (sInstance.mImm == null)
+ Log.w(TAG, "getInstance() is called before initialization");
return sInstance;
}
- private synchronized void init(Context context) {
- mImm = (InputMethodManager) context.getSystemService(
+ public static void init(InputMethodServiceCompatWrapper service) {
+ sInstance.mService = service;
+ sInstance.mImm = (InputMethodManager) service.getSystemService(
Context.INPUT_METHOD_SERVICE);
- if (context instanceof LatinIME) {
- mLatinImePackageName = context.getPackageName();
- }
- mLanguageSwitcherProxy = LanguageSwitcherProxy.getInstance();
+ sInstance.mLatinImePackageName = service.getPackageName();
+ sInstance.mPackageManager = service.getPackageManager();
+ sInstance.mApplicationInfo = service.getApplicationInfo();
+ sInstance.mLanguageSwitcherProxy = LanguageSwitcherProxy.getInstance();
}
public InputMethodSubtypeCompatWrapper getCurrentInputMethodSubtype() {
@@ -196,11 +203,15 @@ public class InputMethodManagerCompatWrapper {
return shortcutMap;
}
+ // We don't call this method when we switch between subtypes within this IME.
public void setInputMethodAndSubtype(
IBinder token, String id, InputMethodSubtypeCompatWrapper subtype) {
+ // TODO: Support subtype change on non-subtype-supported platform.
if (subtype != null && subtype.hasOriginalObject()) {
CompatUtils.invoke(mImm, null, METHOD_setInputMethodAndSubtype,
token, id, subtype.getOriginalObject());
+ } else {
+ mImm.setInputMethod(token, id);
}
}
@@ -222,6 +233,87 @@ public class InputMethodManagerCompatWrapper {
public void showInputMethodPicker() {
if (mImm == null) return;
- mImm.showInputMethodPicker();
+ if (SUBTYPE_SUPPORTED) {
+ mImm.showInputMethodPicker();
+ return;
+ }
+
+ // The code below are based on {@link InputMethodManager#showInputMethodMenuInternal}.
+
+ final InputMethodInfoCompatWrapper myImi = Utils.getInputMethodInfo(
+ this, mLatinImePackageName);
+ final List<InputMethodSubtypeCompatWrapper> myImsList = getEnabledInputMethodSubtypeList(
+ myImi, true);
+ final InputMethodSubtypeCompatWrapper currentIms = getCurrentInputMethodSubtype();
+ final List<InputMethodInfoCompatWrapper> imiList = getEnabledInputMethodList();
+ imiList.remove(myImi);
+ Collections.sort(imiList, new Comparator<InputMethodInfoCompatWrapper>() {
+ @Override
+ public int compare(InputMethodInfoCompatWrapper imi1,
+ InputMethodInfoCompatWrapper imi2) {
+ final CharSequence imiId1 = imi1.loadLabel(mPackageManager) + "/" + imi1.getId();
+ final CharSequence imiId2 = imi2.loadLabel(mPackageManager) + "/" + imi2.getId();
+ return imiId1.toString().compareTo(imiId2.toString());
+ }
+ });
+
+ final int myImsCount = myImsList.size();
+ final int imiCount = imiList.size();
+ final CharSequence[] items = new CharSequence[myImsCount + imiCount];
+
+ int checkedItem = 0;
+ int index = 0;
+ final CharSequence myImiLabel = myImi.loadLabel(mPackageManager);
+ for (int i = 0; i < myImsCount; i++) {
+ InputMethodSubtypeCompatWrapper ims = myImsList.get(i);
+ if (currentIms.equals(ims))
+ checkedItem = index;
+ final CharSequence title = TextUtils.concat(
+ ims.getDisplayName(mService, mLatinImePackageName, mApplicationInfo),
+ " (" + myImiLabel, ")");
+ items[index] = title;
+ index++;
+ }
+
+ for (int i = 0; i < imiCount; i++) {
+ final InputMethodInfoCompatWrapper imi = imiList.get(i);
+ final CharSequence title = imi.loadLabel(mPackageManager);
+ items[index] = title;
+ index++;
+ }
+
+ final OnClickListener buttonListener = new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface di, int whichButton) {
+ final Intent intent = new Intent("android.settings.INPUT_METHOD_SETTINGS");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mService.startActivity(intent);
+ }
+ };
+ final InputMethodServiceCompatWrapper service = mService;
+ final IBinder token = service.getWindow().getWindow().getAttributes().token;
+ final OnClickListener selectionListener = new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface di, int which) {
+ di.dismiss();
+ if (which < myImsCount) {
+ final int imsIndex = which;
+ final InputMethodSubtypeCompatWrapper ims = myImsList.get(imsIndex);
+ service.notifyOnCurrentInputMethodSubtypeChanged(ims);
+ } else {
+ final int imiIndex = which - myImsCount;
+ final InputMethodInfoCompatWrapper imi = imiList.get(imiIndex);
+ setInputMethodAndSubtype(token, imi.getId(), null);
+ }
+ }
+ };
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(mService)
+ .setTitle(mService.getString(R.string.selectInputMethod))
+ .setNeutralButton(R.string.configure_input_method, buttonListener)
+ .setSingleChoiceItems(items, checkedItem, selectionListener);
+ mService.showOptionDialogInternal(builder.create());
}
}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
index 7d8c745c3..7aab66d05 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatWrapper.java
@@ -16,10 +16,15 @@
package com.android.inputmethod.compat;
+import android.app.AlertDialog;
import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
+import android.view.Window;
+import android.view.WindowManager;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.latin.SubtypeSwitcher;
public class InputMethodServiceCompatWrapper extends InputMethodService {
@@ -32,10 +37,33 @@ public class InputMethodServiceCompatWrapper extends InputMethodService {
private InputMethodManagerCompatWrapper mImm;
+ // For compatibility of {@link InputMethodManager#showInputMethodPicker}.
+ // TODO: Move this variable back to LatinIME when this compatibility wrapper is removed.
+ protected AlertDialog mOptionsDialog;
+
+ public void showOptionDialogInternal(AlertDialog dialog) {
+ final IBinder windowToken = KeyboardSwitcher.getInstance().getKeyboardView()
+ .getWindowToken();
+ if (windowToken == null) return;
+
+ dialog.setCancelable(true);
+ dialog.setCanceledOnTouchOutside(true);
+
+ final Window window = dialog.getWindow();
+ final WindowManager.LayoutParams lp = window.getAttributes();
+ lp.token = windowToken;
+ lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+ window.setAttributes(lp);
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+
+ mOptionsDialog = dialog;
+ dialog.show();
+ }
+
@Override
public void onCreate() {
super.onCreate();
- mImm = InputMethodManagerCompatWrapper.getInstance(this);
+ mImm = InputMethodManagerCompatWrapper.getInstance();
}
// When the API level is 10 or previous, notifyOnCurrentInputMethodSubtypeChanged should
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
index 667d86c42..b6b86a4a0 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java
@@ -16,13 +16,16 @@
package com.android.inputmethod.compat;
-import com.android.inputmethod.latin.LatinImeLogger;
-
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.text.TextUtils;
import android.util.Log;
+import com.android.inputmethod.latin.LatinImeLogger;
+
import java.lang.reflect.Method;
import java.util.Arrays;
+import java.util.Locale;
// TODO: Override this class with the concrete implementation if we need to take care of the
// performance.
@@ -50,6 +53,9 @@ public final class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper
CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValueOf", String.class);
private static final Method METHOD_isAuxiliary =
CompatUtils.getMethod(CLASS_InputMethodSubtype, "isAuxiliary");
+ private static final Method METHOD_getDisplayName =
+ CompatUtils.getMethod(CLASS_InputMethodSubtype, "getDisplayName", Context.class,
+ String.class, ApplicationInfo.class);
private final int mDummyNameResId;
private final int mDummyIconResId;
@@ -122,6 +128,28 @@ public final class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper
return (Boolean)CompatUtils.invoke(mObj, false, METHOD_isAuxiliary);
}
+ public CharSequence getDisplayName(Context context, String packageName,
+ ApplicationInfo appInfo) {
+ if (mObj != null) {
+ return (CharSequence)CompatUtils.invoke(
+ mObj, "", METHOD_getDisplayName, context, packageName, appInfo);
+ }
+
+ // The code below are based on {@link InputMethodSubtype#getDisplayName}.
+
+ final Locale locale = new Locale(getLocale());
+ final String localeStr = locale.getDisplayName();
+ if (getNameResId() == 0) {
+ return localeStr;
+ }
+ final CharSequence subtypeName = context.getText(getNameResId());
+ if (!TextUtils.isEmpty(localeStr)) {
+ return String.format(subtypeName.toString(), localeStr);
+ } else {
+ return localeStr;
+ }
+ }
+
public boolean isDummy() {
return !hasOriginalObject();
}
diff --git a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
index 8e2a2e0b8..a6304d877 100644
--- a/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/VibratorCompatWrapper.java
@@ -23,7 +23,7 @@ import java.lang.reflect.Method;
public class VibratorCompatWrapper {
private static final Method METHOD_hasVibrator = CompatUtils.getMethod(Vibrator.class,
- "hasVibrator", int.class);
+ "hasVibrator");
private static final VibratorCompatWrapper sInstance = new VibratorCompatWrapper();
private Vibrator mVibrator;
diff --git a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
index 85993ea4d..9397483ce 100644
--- a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
+++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
@@ -17,11 +17,11 @@
package com.android.inputmethod.deprecated;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
import com.android.inputmethod.deprecated.voice.FieldContext;
import com.android.inputmethod.deprecated.voice.Hints;
import com.android.inputmethod.deprecated.voice.SettingsUtil;
import com.android.inputmethod.deprecated.voice.VoiceInput;
-import com.android.inputmethod.deprecated.voice.VoiceInputLogger;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.latin.EditingUtils;
import com.android.inputmethod.latin.LatinIME;
@@ -71,7 +71,8 @@ import java.util.Map;
public class VoiceProxy implements VoiceInput.UiListener {
private static final VoiceProxy sInstance = new VoiceProxy();
- public static final boolean VOICE_INSTALLED = true;
+ public static final boolean VOICE_INSTALLED =
+ !InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED;
private static final boolean ENABLE_VOICE_BUTTON = true;
private static final String PREF_VOICE_MODE = "voice_mode";
// Whether or not the user has used voice input before (and thus, whether to show the
@@ -125,24 +126,23 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
private void initInternal(LatinIME service, SharedPreferences prefs, UIHandler h) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
mService = service;
mHandler = h;
mMinimumVoiceRecognitionViewHeightPixel = Utils.dipToPixel(
Utils.getDipScale(service), RECOGNITIONVIEW_MINIMUM_HEIGHT_DIP);
- mImm = InputMethodManagerCompatWrapper.getInstance(service);
+ mImm = InputMethodManagerCompatWrapper.getInstance();
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
- if (VOICE_INSTALLED) {
- mVoiceInput = new VoiceInput(service, this);
- mHints = new Hints(service, prefs, new Hints.Display() {
- @Override
- public void showHint(int viewResource) {
- View view = LayoutInflater.from(mService).inflate(viewResource, null);
-// mService.setCandidatesView(view);
-// mService.setCandidatesViewShown(true);
- mIsShowingHint = true;
- }
- });
- }
+ mVoiceInput = new VoiceInput(service, this);
+ mHints = new Hints(service, prefs, new Hints.Display() {
+ @Override
+ public void showHint(int viewResource) {
+ View view = LayoutInflater.from(mService).inflate(viewResource, null);
+ mIsShowingHint = true;
+ }
+ });
}
private VoiceProxy() {
@@ -158,7 +158,10 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void flushVoiceInputLogs(boolean configurationChanged) {
- if (VOICE_INSTALLED && !configurationChanged) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
+ if (!configurationChanged) {
if (mAfterVoiceInput) {
mVoiceInput.flushAllTextModificationCounters();
mVoiceInput.logInputEnded();
@@ -170,6 +173,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
public void flushAndLogAllTextModificationCounters(int index, CharSequence suggestion,
String wordSeparators) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (mAfterVoiceInput && mShowingVoiceSuggestions) {
mVoiceInput.flushAllTextModificationCounters();
// send this intent AFTER logging any prior aggregated edits.
@@ -298,6 +304,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void showPunctuationHintIfNecessary() {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
InputConnection ic = mService.getCurrentInputConnection();
if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) {
if (mHints.showPunctuationHintIfNecessary(ic)) {
@@ -308,6 +317,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void hideVoiceWindow(boolean configurationChanging) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (!configurationChanging) {
if (mAfterVoiceInput)
mVoiceInput.logInputEnded();
@@ -324,6 +336,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void setCursorAndSelection(int newSelEnd, int newSelStart) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (mAfterVoiceInput) {
mVoiceInput.setCursorPos(newSelEnd);
mVoiceInput.setSelectionSpan(newSelEnd - newSelStart);
@@ -382,7 +397,10 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public boolean logAndRevertVoiceInput() {
- if (VOICE_INSTALLED && mVoiceInputHighlighted) {
+ if (!VOICE_INSTALLED) {
+ return false;
+ }
+ if (mVoiceInputHighlighted) {
mVoiceInput.incrementTextModificationDeleteCount(
mVoiceResults.candidates.get(0).toString().length());
revertVoiceInput();
@@ -393,6 +411,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void rememberReplacedWord(CharSequence suggestion,String wordSeparators) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (mShowingVoiceSuggestions) {
// Retain the replaced word in the alternatives array.
String wordToBeReplaced = EditingUtils.getWordAtCursor(
@@ -419,6 +440,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
* @return true if an alternative was found, false otherwise.
*/
public boolean applyVoiceAlternatives(EditingUtils.SelectedWord touching) {
+ if (!VOICE_INSTALLED) {
+ return false;
+ }
// Search for result in spoken word alternatives
String selectedWord = touching.mWord.toString().trim();
if (!mWordToSuggestions.containsKey(selectedWord)) {
@@ -448,6 +472,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void handleBackspace() {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (mAfterVoiceInput) {
// Don't log delete if the user is pressing delete at
// the beginning of the text box (hence not deleting anything)
@@ -462,6 +489,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void handleCharacter() {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
commitVoiceInput();
if (mAfterVoiceInput) {
// Assume input length is 1. This assumption fails for smiley face insertions.
@@ -470,6 +500,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void handleSeparator() {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
commitVoiceInput();
if (mAfterVoiceInput){
// Assume input length is 1. This assumption fails for smiley face insertions.
@@ -478,13 +511,19 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void handleClose() {
- if (VOICE_INSTALLED & mRecognizing) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
+ if (mRecognizing) {
mVoiceInput.cancel();
}
}
public void handleVoiceResults(boolean capitalizeFirstWord) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
mAfterVoiceInput = true;
mImmediatelyAfterVoiceInput = true;
@@ -523,6 +562,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void switchToRecognitionStatusView(final Configuration configuration) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -567,6 +609,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
private void switchToLastInputMethod() {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
final IBinder token = mService.getWindow().getWindow().getAttributes().token;
new AsyncTask<Void, Void, Boolean>() {
@Override
@@ -632,14 +677,15 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void startListening(final boolean swipe, IBinder token) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
// TODO: remove swipe which is no longer used.
- if (VOICE_INSTALLED) {
- if (needsToShowWarningDialog()) {
- // Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
- showVoiceWarningDialog(swipe, token);
- } else {
- reallyStartListening(swipe);
- }
+ if (needsToShowWarningDialog()) {
+ // Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
+ showVoiceWarningDialog(swipe, token);
+ } else {
+ reallyStartListening(swipe);
}
}
@@ -659,7 +705,14 @@ public class VoiceProxy implements VoiceInput.UiListener {
&& SpeechRecognizer.isRecognitionAvailable(mService);
}
+ public static boolean isRecognitionAvailable(Context context) {
+ return SpeechRecognizer.isRecognitionAvailable(context);
+ }
+
public void loadSettings(EditorInfo attribute, SharedPreferences sp) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
mHasUsedVoiceInput = sp.getBoolean(PREF_HAS_USED_VOICE_INPUT, false);
mHasUsedVoiceInputUnsupportedLocale =
sp.getBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, false);
@@ -667,22 +720,26 @@ public class VoiceProxy implements VoiceInput.UiListener {
mLocaleSupportedForVoiceInput = SubtypeSwitcher.getInstance().isVoiceSupported(
SubtypeSwitcher.getInstance().getInputLocaleStr());
- if (VOICE_INSTALLED) {
- final String voiceMode = sp.getString(PREF_VOICE_MODE,
- mService.getString(R.string.voice_mode_main));
- mVoiceButtonEnabled = !voiceMode.equals(mService.getString(R.string.voice_mode_off))
- && shouldShowVoiceButton(makeFieldContext(), attribute);
- mVoiceButtonOnPrimary = voiceMode.equals(mService.getString(R.string.voice_mode_main));
- }
+ final String voiceMode = sp.getString(PREF_VOICE_MODE,
+ mService.getString(R.string.voice_mode_main));
+ mVoiceButtonEnabled = !voiceMode.equals(mService.getString(R.string.voice_mode_off))
+ && shouldShowVoiceButton(makeFieldContext(), attribute);
+ mVoiceButtonOnPrimary = voiceMode.equals(mService.getString(R.string.voice_mode_main));
}
public void destroy() {
- if (VOICE_INSTALLED && mVoiceInput != null) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
+ if (mVoiceInput != null) {
mVoiceInput.destroy();
}
}
public void onStartInputView(IBinder keyboardViewToken) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
// If keyboardViewToken is null, keyboardView is not attached but voiceView is attached.
IBinder windowToken = keyboardViewToken != null ? keyboardViewToken
: mVoiceInput.getView().getWindowToken();
@@ -699,12 +756,18 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void onAttachedToWindow() {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
// After onAttachedToWindow, we can show the voice warning dialog. See startListening()
// above.
VoiceInputWrapper.getInstance().setVoiceInput(mVoiceInput, mSubtypeSwitcher);
}
public void onConfigurationChanged(Configuration configuration) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (mRecognizing) {
switchToRecognitionStatusView(configuration);
}
@@ -712,6 +775,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
@Override
public void onCancelVoice() {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (mRecognizing) {
if (mSubtypeSwitcher.isVoiceMode()) {
// If voice mode is being canceled within LatinIME (i.e. time-out or user
@@ -733,6 +799,9 @@ public class VoiceProxy implements VoiceInput.UiListener {
@Override
public void onVoiceResults(List<String> candidates,
Map<String, List<CharSequence>> alternatives) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (!mRecognizing) {
return;
}
@@ -748,59 +817,22 @@ public class VoiceProxy implements VoiceInput.UiListener {
switcher.getEnabledLanguages());
}
- private class VoiceResults {
+ // TODO: make this private (proguard issue)
+ public static class VoiceResults {
List<String> candidates;
Map<String, List<CharSequence>> alternatives;
}
- public static class VoiceLoggerWrapper {
- private static final VoiceLoggerWrapper sLoggerWrapperInstance = new VoiceLoggerWrapper();
- private VoiceInputLogger mLogger;
-
- public static VoiceLoggerWrapper getInstance(Context context) {
- if (sLoggerWrapperInstance.mLogger == null) {
- // Not thread safe, but it's ok.
- sLoggerWrapperInstance.mLogger = VoiceInputLogger.getLogger(context);
- }
- return sLoggerWrapperInstance;
- }
-
- // private for the singleton
- private VoiceLoggerWrapper() {
- }
-
- public void settingsWarningDialogCancel() {
- mLogger.settingsWarningDialogCancel();
- }
-
- public void settingsWarningDialogOk() {
- mLogger.settingsWarningDialogOk();
- }
-
- public void settingsWarningDialogShown() {
- mLogger.settingsWarningDialogShown();
- }
-
- public void settingsWarningDialogDismissed() {
- mLogger.settingsWarningDialogDismissed();
- }
-
- public void voiceInputSettingEnabled(boolean enabled) {
- if (enabled) {
- mLogger.voiceInputSettingEnabled();
- } else {
- mLogger.voiceInputSettingDisabled();
- }
- }
- }
-
public static class VoiceInputWrapper {
private static final VoiceInputWrapper sInputWrapperInstance = new VoiceInputWrapper();
private VoiceInput mVoiceInput;
public static VoiceInputWrapper getInstance() {
return sInputWrapperInstance;
}
- public void setVoiceInput(VoiceInput voiceInput, SubtypeSwitcher switcher) {
+ private void setVoiceInput(VoiceInput voiceInput, SubtypeSwitcher switcher) {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (mVoiceInput == null && voiceInput != null) {
mVoiceInput = voiceInput;
}
@@ -811,10 +843,16 @@ public class VoiceProxy implements VoiceInput.UiListener {
}
public void cancel() {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (mVoiceInput != null) mVoiceInput.cancel();
}
public void reset() {
+ if (!VOICE_INSTALLED) {
+ return;
+ }
if (mVoiceInput != null) mVoiceInput.reset();
}
}
diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
index cf6cd0f5e..e75559e62 100644
--- a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
+++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
@@ -16,7 +16,7 @@
package com.android.inputmethod.deprecated.languageswitcher;
-import com.android.inputmethod.keyboard.internal.KeyboardParser;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.latin.DictionaryFactory;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.Settings;
@@ -162,7 +162,7 @@ public class InputLanguageSelection extends PreferenceActivity {
try {
final String localeStr = locale.toString();
- final String[] layoutCountryCodes = KeyboardParser.parseKeyboardLocale(
+ final String[] layoutCountryCodes = KeyboardBuilder.parseKeyboardLocale(
this, R.xml.kbd_qwerty).split(",", -1);
if (!TextUtils.isEmpty(localeStr) && layoutCountryCodes.length > 0) {
for (String s : layoutCountryCodes) {
diff --git a/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java
index 5e6c87044..f33a46277 100644
--- a/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java
+++ b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java
@@ -57,6 +57,7 @@ public class RecorrectionSuggestionEntries {
private static SuggestedWords.Builder getTypedSuggestions(
Suggest suggest, KeyboardSwitcher keyboardSwitcher, WordComposer word) {
- return suggest.getSuggestedWordBuilder(keyboardSwitcher.getKeyboardView(), word, null);
+ return suggest.getSuggestedWordBuilder(keyboardSwitcher.getKeyboardView(), word, null,
+ keyboardSwitcher.getLatinKeyboard().getProximityInfo());
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 45bf68cdf..397b7b16b 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -27,13 +27,15 @@ import android.util.Xml;
import com.android.inputmethod.keyboard.internal.KeyStyles;
import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardParser;
-import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException;
import com.android.inputmethod.keyboard.internal.PopupCharactersParser;
import com.android.inputmethod.keyboard.internal.Row;
import com.android.inputmethod.latin.R;
-import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
/**
* Class for describing the position and characteristics of a single key in the keyboard.
@@ -49,10 +51,10 @@ public class Key {
/** Hint label to display on the key in conjunction with the label */
public final CharSequence mHintLabel;
/** Option of the label */
- public final int mLabelOption;
- public static final int LABEL_OPTION_ALIGN_LEFT = 0x01;
- public static final int LABEL_OPTION_ALIGN_RIGHT = 0x02;
- public static final int LABEL_OPTION_ALIGN_LEFT_OF_CENTER = 0x08;
+ private final int mLabelOption;
+ private static final int LABEL_OPTION_ALIGN_LEFT = 0x01;
+ private static final int LABEL_OPTION_ALIGN_RIGHT = 0x02;
+ private static final int LABEL_OPTION_ALIGN_LEFT_OF_CENTER = 0x08;
private static final int LABEL_OPTION_LARGE_LETTER = 0x10;
private static final int LABEL_OPTION_FONT_NORMAL = 0x20;
private static final int LABEL_OPTION_FONT_MONO_SPACE = 0x40;
@@ -61,6 +63,8 @@ public class Key {
private static final int LABEL_OPTION_HAS_POPUP_HINT = 0x200;
private static final int LABEL_OPTION_HAS_UPPERCASE_LETTER = 0x400;
private static final int LABEL_OPTION_HAS_HINT_LABEL = 0x800;
+ private static final int LABEL_OPTION_WITH_ICON_LEFT = 0x1000;
+ private static final int LABEL_OPTION_WITH_ICON_RIGHT = 0x2000;
/** Icon to display instead of a label. Icon takes precedence over a label */
private Drawable mIcon;
@@ -72,7 +76,9 @@ public class Key {
/** Height of the key, not including the gap */
public final int mHeight;
/** The horizontal gap around this key */
- public final int mGap;
+ public final int mHorizontalGap;
+ /** The vertical gap below this key */
+ public final int mVerticalGap;
/** The visual insets */
public final int mVisualInsetsLeft;
public final int mVisualInsetsRight;
@@ -95,21 +101,20 @@ public class Key {
* {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT},
* {@link Keyboard#EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM}.
*/
- public final int mEdgeFlags;
+ private int mEdgeFlags;
/** Whether this is a functional key which has different key top than normal key */
public final boolean mFunctional;
/** Whether this key repeats itself when held down */
public final boolean mRepeatable;
- /** The Keyboard that this key belongs to */
- private final Keyboard mKeyboard;
-
/** The current pressed state of this key */
private boolean mPressed;
/** If this is a sticky key, is its highlight on? */
private boolean mHighlightOn;
/** Key is enabled and responds on press */
private boolean mEnabled = true;
+ /** Whether this key needs to show the "..." popup hint for special purposes */
+ private boolean mNeedsSpecialPopupHint;
// keyWidth constants
private static final int KEYWIDTH_FILL_RIGHT = 0;
@@ -153,16 +158,50 @@ public class Key {
android.R.attr.state_pressed
};
+ // RTL parenthesis character swapping map.
+ private static final Map<Integer, Integer> sRtlParenthesisMap = new HashMap<Integer, Integer>();
+
+ static {
+ // The all letters need to be mirrored are found at
+ // http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
+ addRtlParenthesisPair('(', ')');
+ addRtlParenthesisPair('[', ']');
+ addRtlParenthesisPair('{', '}');
+ addRtlParenthesisPair('<', '>');
+ // \u00ab: LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ // \u00bb: RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ addRtlParenthesisPair('\u00ab', '\u00bb');
+ // \u2039: SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ // \u203a: SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ addRtlParenthesisPair('\u2039', '\u203a');
+ // \u2264: LESS-THAN OR EQUAL TO
+ // \u2265: GREATER-THAN OR EQUAL TO
+ addRtlParenthesisPair('\u2264', '\u2265');
+ }
+
+ private static void addRtlParenthesisPair(int left, int right) {
+ sRtlParenthesisMap.put(left, right);
+ sRtlParenthesisMap.put(right, left);
+ }
+
+ public static int getRtlParenthesisCode(int code) {
+ if (sRtlParenthesisMap.containsKey(code)) {
+ return sRtlParenthesisMap.get(code);
+ } else {
+ return code;
+ }
+ }
+
/**
* This constructor is being used only for key in popup mini keyboard.
*/
- public Key(Resources res, Keyboard keyboard, CharSequence popupCharacter, int x, int y,
+ public Key(Resources res, KeyboardParams params, CharSequence popupCharacter, int x, int y,
int width, int height, int edgeFlags) {
- mKeyboard = keyboard;
- mHeight = height - keyboard.getVerticalGap();
- mGap = keyboard.getHorizontalGap();
+ mHeight = height - params.mVerticalGap;
+ mHorizontalGap = params.mHorizontalGap;
+ mVerticalGap = params.mVerticalGap;
mVisualInsetsLeft = mVisualInsetsRight = 0;
- mWidth = width - mGap;
+ mWidth = width - mHorizontalGap;
mEdgeFlags = edgeFlags;
mHintLabel = null;
mLabelOption = 0;
@@ -174,10 +213,11 @@ public class Key {
final String popupSpecification = popupCharacter.toString();
mLabel = PopupCharactersParser.getLabel(popupSpecification);
mOutputText = PopupCharactersParser.getOutputText(popupSpecification);
- mCode = PopupCharactersParser.getCode(res, popupSpecification);
- mIcon = keyboard.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification));
+ final int code = PopupCharactersParser.getCode(res, popupSpecification);
+ mCode = params.mIsRtlKeyboard ? getRtlParenthesisCode(code) : code;
+ mIcon = params.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification));
// Horizontal gap is divided equally to both sides of the key.
- mX = x + mGap / 2;
+ mX = x + mHorizontalGap / 2;
mY = y;
}
@@ -185,30 +225,30 @@ public class Key {
* Create a key with the given top-left coordinate and extract its attributes from the XML
* parser.
* @param res resources associated with the caller's context
- * @param row the row that this key belongs to. The row must already be attached to
- * a {@link Keyboard}.
+ * @param params the keyboard building parameters.
+ * @param row the row that this key belongs to.
* @param x the x coordinate of the top-left
* @param y the y coordinate of the top-left
* @param parser the XML parser containing the attributes for this key
* @param keyStyles active key styles set
*/
- public Key(Resources res, Row row, int x, int y, XmlResourceParser parser,
- KeyStyles keyStyles) {
- mKeyboard = row.getKeyboard();
+ public Key(Resources res, KeyboardParams params, Row row, int x, int y,
+ XmlResourceParser parser, KeyStyles keyStyles) {
final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard);
int keyWidth;
try {
- mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr,
+ mHeight = KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_rowHeight,
- mKeyboard.getKeyboardHeight(), row.mDefaultHeight) - row.mVerticalGap;
- mGap = KeyboardParser.getDimensionOrFraction(keyboardAttr,
+ params.mHeight, row.mRowHeight) - params.mVerticalGap;
+ mHorizontalGap = KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_horizontalGap,
- mKeyboard.getDisplayWidth(), row.mDefaultHorizontalGap);
- keyWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr,
+ params.mWidth, params.mHorizontalGap);
+ mVerticalGap = params.mVerticalGap;
+ keyWidth = KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_keyWidth,
- mKeyboard.getDisplayWidth(), row.mDefaultWidth);
+ params.mWidth, row.mDefaultKeyWidth);
} finally {
keyboardAttr.recycle();
}
@@ -226,8 +266,8 @@ public class Key {
style = keyStyles.getEmptyKeyStyle();
}
- final int keyboardWidth = mKeyboard.getDisplayWidth();
- int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr,
+ final int keyboardWidth = params.mOccupiedWidth;
+ int keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr,
R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x);
if (keyXPos < 0) {
// If keyXPos is negative, the actual x-coordinate will be k + keyXPos.
@@ -251,44 +291,52 @@ public class Key {
}
// Horizontal gap is divided equally to both sides of the key.
- mX = keyXPos + mGap / 2;
+ mX = keyXPos + mHorizontalGap / 2;
mY = y;
- mWidth = keyWidth - mGap;
+ mWidth = keyWidth - mHorizontalGap;
- final CharSequence[] popupCharacters = style.getTextArray(keyAttr,
- R.styleable.Keyboard_Key_popupCharacters);
+ CharSequence[] popupCharacters = style.getTextArray(
+ keyAttr, R.styleable.Keyboard_Key_popupCharacters);
+ if (params.mId.mPasswordInput) {
+ popupCharacters = PopupCharactersParser.filterOut(
+ res, popupCharacters, PopupCharactersParser.NON_ASCII_FILTER);
+ }
// In Arabic symbol layouts, we'd like to keep digits in popup characters regardless of
// config_digit_popup_characters_enabled.
- if (mKeyboard.mId.isAlphabetKeyboard() && !res.getBoolean(
+ if (params.mId.isAlphabetKeyboard() && !res.getBoolean(
R.bool.config_digit_popup_characters_enabled)) {
- mPopupCharacters = filterOutDigitPopupCharacters(popupCharacters);
+ mPopupCharacters = PopupCharactersParser.filterOut(
+ res, popupCharacters, PopupCharactersParser.DIGIT_FILTER);
} else {
mPopupCharacters = popupCharacters;
}
mMaxPopupColumn = style.getInt(keyboardAttr,
R.styleable.Keyboard_Key_maxPopupKeyboardColumn,
- mKeyboard.getMaxPopupKeyboardColumn());
+ params.mMaxPopupColumn);
mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false);
mFunctional = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isFunctional, false);
mSticky = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky, false);
mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true);
- mEdgeFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyEdgeFlags, 0)
- | row.mRowEdgeFlags;
+ mEdgeFlags = 0;
- final KeyboardIconsSet iconsSet = mKeyboard.mIconsSet;
- mVisualInsetsLeft = KeyboardParser.getDimensionOrFraction(keyAttr,
+ final KeyboardIconsSet iconsSet = params.mIconsSet;
+ mVisualInsetsLeft = KeyboardBuilder.getDimensionOrFraction(keyAttr,
R.styleable.Keyboard_Key_visualInsetsLeft, keyboardWidth, 0);
- mVisualInsetsRight = KeyboardParser.getDimensionOrFraction(keyAttr,
+ mVisualInsetsRight = KeyboardBuilder.getDimensionOrFraction(keyAttr,
R.styleable.Keyboard_Key_visualInsetsRight, keyboardWidth, 0);
mPreviewIcon = iconsSet.getIcon(style.getInt(
keyAttr, R.styleable.Keyboard_Key_keyIconPreview,
KeyboardIconsSet.ICON_UNDEFINED));
- Keyboard.setDefaultBounds(mPreviewIcon);
mIcon = iconsSet.getIcon(style.getInt(
keyAttr, R.styleable.Keyboard_Key_keyIcon,
KeyboardIconsSet.ICON_UNDEFINED));
- Keyboard.setDefaultBounds(mIcon);
+ final int shiftedIconId = style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconShifted,
+ KeyboardIconsSet.ICON_UNDEFINED);
+ if (shiftedIconId != KeyboardIconsSet.ICON_UNDEFINED) {
+ final Drawable shiftedIcon = iconsSet.getIcon(shiftedIconId);
+ params.addShiftedIcon(this, shiftedIcon);
+ }
mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel);
@@ -299,25 +347,20 @@ public class Key {
final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code,
Keyboard.CODE_UNSPECIFIED);
if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) {
- mCode = mLabel.charAt(0);
+ final int firstChar = mLabel.charAt(0);
+ mCode = params.mIsRtlKeyboard ? getRtlParenthesisCode(firstChar) : firstChar;
} else if (code != Keyboard.CODE_UNSPECIFIED) {
mCode = code;
} else {
mCode = Keyboard.CODE_DUMMY;
}
-
- final Drawable shiftedIcon = iconsSet.getIcon(style.getInt(
- keyAttr, R.styleable.Keyboard_Key_keyIconShifted,
- KeyboardIconsSet.ICON_UNDEFINED));
- if (shiftedIcon != null)
- mKeyboard.getShiftedIcons().put(this, shiftedIcon);
} finally {
keyAttr.recycle();
}
}
- public CharSequence getCaseAdjustedLabel() {
- return mKeyboard.adjustLabelCase(mLabel);
+ public void addEdgeFlags(int flags) {
+ mEdgeFlags |= flags;
}
public Typeface selectTypeface(Typeface defaultTypeface) {
@@ -345,10 +388,30 @@ public class Key {
}
}
+ public boolean isAlignLeft() {
+ return (mLabelOption & LABEL_OPTION_ALIGN_LEFT) != 0;
+ }
+
+ public boolean isAlignRight() {
+ return (mLabelOption & LABEL_OPTION_ALIGN_RIGHT) != 0;
+ }
+
+ public boolean isAlignLeftOfCenter() {
+ return (mLabelOption & LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0;
+ }
+
public boolean hasPopupHint() {
return (mLabelOption & LABEL_OPTION_HAS_POPUP_HINT) != 0;
}
+ public void setNeedsSpecialPopupHint(boolean needsSpecialPopupHint) {
+ mNeedsSpecialPopupHint = needsSpecialPopupHint;
+ }
+
+ public boolean needsSpecialPopupHint() {
+ return mNeedsSpecialPopupHint;
+ }
+
public boolean hasUppercaseLetter() {
return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0;
}
@@ -357,34 +420,12 @@ public class Key {
return (mLabelOption & LABEL_OPTION_HAS_HINT_LABEL) != 0;
}
- private static boolean isDigitPopupCharacter(CharSequence label) {
- return label != null && label.length() == 1 && Character.isDigit(label.charAt(0));
+ public boolean hasLabelWithIconLeft() {
+ return (mLabelOption & LABEL_OPTION_WITH_ICON_LEFT) != 0;
}
- private static CharSequence[] filterOutDigitPopupCharacters(CharSequence[] popupCharacters) {
- if (popupCharacters == null || popupCharacters.length < 1)
- return null;
- if (popupCharacters.length == 1 && isDigitPopupCharacter(
- PopupCharactersParser.getLabel(popupCharacters[0].toString())))
- return null;
- ArrayList<CharSequence> filtered = null;
- for (int i = 0; i < popupCharacters.length; i++) {
- final CharSequence popupSpec = popupCharacters[i];
- if (isDigitPopupCharacter(PopupCharactersParser.getLabel(popupSpec.toString()))) {
- if (filtered == null) {
- filtered = new ArrayList<CharSequence>();
- for (int j = 0; j < i; j++)
- filtered.add(popupCharacters[j]);
- }
- } else if (filtered != null) {
- filtered.add(popupSpec);
- }
- }
- if (filtered == null)
- return popupCharacters;
- if (filtered.size() == 0)
- return null;
- return filtered.toArray(new CharSequence[filtered.size()]);
+ public boolean hasLabelWithIconRight() {
+ return (mLabelOption & LABEL_OPTION_WITH_ICON_RIGHT) != 0;
}
public Drawable getIcon() {
@@ -441,15 +482,18 @@ public class Key {
* assume that all points between the key and the edge are considered to be on the key.
*/
public boolean isOnKey(int x, int y) {
+ final int left = mX - mHorizontalGap / 2;
+ final int right = left + mWidth + mHorizontalGap;
+ final int top = mY;
+ final int bottom = top + mHeight + mVerticalGap;
final int flags = mEdgeFlags;
+ if (flags == 0) {
+ return x >= left && x <= right && y >= top && y <= bottom;
+ }
final boolean leftEdge = (flags & Keyboard.EDGE_LEFT) != 0;
final boolean rightEdge = (flags & Keyboard.EDGE_RIGHT) != 0;
final boolean topEdge = (flags & Keyboard.EDGE_TOP) != 0;
final boolean bottomEdge = (flags & Keyboard.EDGE_BOTTOM) != 0;
- final int left = mX - mGap / 2;
- final int right = left + mWidth + mGap;
- final int top = mY;
- final int bottom = top + mHeight + mKeyboard.getVerticalGap();
// In order to mitigate rounding errors, we use (left <= x <= right) here.
return (x >= left || leftEdge) && (x <= right || rightEdge)
&& (y >= top || topEdge) && (y <= bottom || bottomEdge);
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 6d25025c5..0a3acb48b 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -57,7 +57,7 @@ public class KeyDetector {
mCorrectionX = (int)correctionX;
mCorrectionY = (int)correctionY;
mKeyboard = keyboard;
- final int threshold = keyboard.getMostCommonKeyWidth();
+ final int threshold = keyboard.mMostCommonKeyWidth;
mProximityThresholdSquare = threshold * threshold;
}
@@ -153,7 +153,7 @@ public class KeyDetector {
}
private void getNearbyKeyCodes(final int[] allCodes) {
- final List<Key> keys = getKeyboard().getKeys();
+ final List<Key> keys = getKeyboard().mKeys;
final int[] indices = mIndices;
// allCodes[0] should always have the key code even if it is a non-letter key.
@@ -187,7 +187,7 @@ public class KeyDetector {
* @return The nearest key index
*/
public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
- final List<Key> keys = getKeyboard().getKeys();
+ final List<Key> keys = getKeyboard().mKeys;
final int touchX = getTouchX(x);
final int touchY = getTouchY(y);
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 3e45793cb..f8e08b06a 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -16,25 +16,17 @@
package com.android.inputmethod.keyboard;
-import android.content.Context;
-import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
-import android.util.Log;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardParser;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.keyboard.internal.KeyboardShiftState;
-import com.android.inputmethod.latin.R;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
@@ -55,8 +47,6 @@ import java.util.Map;
* </pre>
*/
public class Keyboard {
- private static final String TAG = Keyboard.class.getSimpleName();
-
public static final int EDGE_LEFT = 0x01;
public static final int EDGE_RIGHT = 0x02;
public static final int EDGE_TOP = 0x04;
@@ -77,6 +67,8 @@ public class Keyboard {
public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
public static final int CODE_CLOSING_CURLY_BRACKET = '}';
public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
+ public static final int CODE_DIGIT0 = '0';
+ public static final int CODE_PLUS = '+';
/** Special keys code. These should be aligned with values/keycodes.xml */
@@ -87,216 +79,94 @@ public class Keyboard {
public static final int CODE_CANCEL = -4;
public static final int CODE_DELETE = -5;
public static final int CODE_SETTINGS = -6;
- public static final int CODE_SETTINGS_LONGPRESS = -7;
- public static final int CODE_SHORTCUT = -8;
+ public static final int CODE_SHORTCUT = -7;
// Code value representing the code is not specified.
public static final int CODE_UNSPECIFIED = -99;
- /** Horizontal gap default for all rows */
- private int mDefaultHorizontalGap;
-
- /** Default key width */
- private int mDefaultWidth;
-
- /** Default key height */
- private int mDefaultHeight;
-
- /** Default gap between rows */
- private int mDefaultVerticalGap;
-
- /** Popup keyboard template */
- private int mPopupKeyboardResId;
-
- /** Maximum column for popup keyboard */
- private int mMaxPopupColumn;
-
- /** List of shift keys in this keyboard and its icons and state */
- private final List<Key> mShiftKeys = new ArrayList<Key>();
- private final HashMap<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>();
- private final HashMap<Key, Drawable> mNormalShiftIcons = new HashMap<Key, Drawable>();
- private final HashSet<Key> mShiftLockEnabled = new HashSet<Key>();
- private final KeyboardShiftState mShiftState = new KeyboardShiftState();
+ public final KeyboardId mId;
/** Total height of the keyboard, including the padding and keys */
- private int mTotalHeight;
-
- /**
- * Total width (minimum width) of the keyboard, including left side gaps and keys, but not any
- * gaps on the right side.
- */
- private int mMinWidth;
+ public final int mOccupiedHeight;
+ /** Total width of the keyboard, including the padding and keys */
+ public final int mOccupiedWidth;
- /** List of keys in this keyboard */
- private final List<Key> mKeys = new ArrayList<Key>();
+ public final int mHeight;
+ public final int mWidth;
- /** Width of the screen available to fit the keyboard */
- private final int mDisplayWidth;
+ /** Default row height */
+ public final int mDefaultRowHeight;
- /** Height of the screen */
- private final int mDisplayHeight;
+ /** Default gap between rows */
+ public final int mVerticalGap;
- /** Height of keyboard */
- private int mKeyboardHeight;
+ public final int mMostCommonKeyWidth;
- private int mMostCommonKeyWidth = 0;
+ /** Popup keyboard template */
+ public final int mPopupKeyboardResId;
- public final KeyboardId mId;
+ /** Maximum column for popup keyboard */
+ public final int mMaxPopupColumn;
- public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
+ /** True if Right-To-Left keyboard */
+ public final boolean mIsRtlKeyboard;
- // Variables for pre-computing nearest keys.
+ /** List of keys and icons in this keyboard */
+ public final List<Key> mKeys;
+ public final List<Key> mShiftKeys;
+ public final Set<Key> mShiftLockKeys;
+ public final Map<Key, Drawable> mShiftedIcons;
+ public final Map<Key, Drawable> mUnshiftedIcons;
+ public final KeyboardIconsSet mIconsSet;
- // TODO: Change GRID_WIDTH and GRID_HEIGHT to private.
- public final int GRID_WIDTH;
- public final int GRID_HEIGHT;
+ private final KeyboardShiftState mShiftState = new KeyboardShiftState();
private final ProximityInfo mProximityInfo;
- /**
- * Creates a keyboard from the given xml key layout file.
- * @param context the application or service context
- * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
- * @param id keyboard identifier
- * @param width keyboard width
- */
+ public Keyboard(KeyboardParams params) {
+ mId = params.mId;
+ mOccupiedHeight = params.mOccupiedHeight;
+ mOccupiedWidth = params.mOccupiedWidth;
+ mHeight = params.mHeight;
+ mWidth = params.mWidth;
+ mMostCommonKeyWidth = params.mMostCommonKeyWidth;
+ mIsRtlKeyboard = params.mIsRtlKeyboard;
+ mPopupKeyboardResId = params.mPopupKeyboardResId;
+ mMaxPopupColumn = params.mMaxPopupColumn;
+
+ mDefaultRowHeight = params.mDefaultRowHeight;
+ mVerticalGap = params.mVerticalGap;
+
+ mKeys = Collections.unmodifiableList(params.mKeys);
+ mShiftKeys = Collections.unmodifiableList(params.mShiftKeys);
+ mShiftLockKeys = Collections.unmodifiableSet(params.mShiftLockKeys);
+ mShiftedIcons = Collections.unmodifiableMap(params.mShiftedIcons);
+ mUnshiftedIcons = Collections.unmodifiableMap(params.mUnshiftedIcons);
+ mIconsSet = params.mIconsSet;
- public Keyboard(Context context, int xmlLayoutResId, KeyboardId id, int width) {
- final Resources res = context.getResources();
- GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
- GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
-
- final int horizontalEdgesPadding = (int)res.getDimension(
- R.dimen.keyboard_horizontal_edges_padding);
- mDisplayWidth = width - horizontalEdgesPadding * 2;
- // TODO: Adjust the height by referring to the height of area available for drawing as well.
- mDisplayHeight = res.getDisplayMetrics().heightPixels;
-
- mDefaultHorizontalGap = 0;
- setKeyWidth(mDisplayWidth / 10);
- mDefaultVerticalGap = 0;
- mDefaultHeight = mDefaultWidth;
- mId = id;
- loadKeyboard(context, xmlLayoutResId);
mProximityInfo = new ProximityInfo(
- GRID_WIDTH, GRID_HEIGHT, getMinWidth(), getHeight(), getKeyWidth(), mKeys);
- }
-
- public int getProximityInfo() {
- return mProximityInfo.getNativeProximityInfo();
- }
-
- public List<Key> getKeys() {
- return mKeys;
- }
-
- public int getHorizontalGap() {
- return mDefaultHorizontalGap;
- }
-
- public void setHorizontalGap(int gap) {
- mDefaultHorizontalGap = gap;
- }
-
- public int getVerticalGap() {
- return mDefaultVerticalGap;
- }
-
- public void setVerticalGap(int gap) {
- mDefaultVerticalGap = gap;
- }
-
- public int getRowHeight() {
- return mDefaultHeight;
- }
-
- public void setRowHeight(int height) {
- mDefaultHeight = height;
- }
-
- public int getKeyWidth() {
- return mDefaultWidth;
- }
-
- public void setKeyWidth(int width) {
- mDefaultWidth = width;
- }
-
- /**
- * Returns the total height of the keyboard
- * @return the total height of the keyboard
- */
- public int getHeight() {
- return mTotalHeight;
- }
-
- public void setHeight(int height) {
- mTotalHeight = height;
- }
-
- public int getMinWidth() {
- return mMinWidth;
- }
-
- public void setMinWidth(int minWidth) {
- mMinWidth = minWidth;
- }
-
- public int getDisplayHeight() {
- return mDisplayHeight;
- }
-
- public int getDisplayWidth() {
- return mDisplayWidth;
+ params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
+ mMostCommonKeyWidth, mKeys);
}
- public int getKeyboardHeight() {
- return mKeyboardHeight;
+ public ProximityInfo getProximityInfo() {
+ return mProximityInfo;
}
- public void setKeyboardHeight(int height) {
- mKeyboardHeight = height;
- }
-
- public int getPopupKeyboardResId() {
- return mPopupKeyboardResId;
- }
-
- public void setPopupKeyboardResId(int resId) {
- mPopupKeyboardResId = resId;
- }
-
- public int getMaxPopupKeyboardColumn() {
- return mMaxPopupColumn;
- }
-
- public void setMaxPopupKeyboardColumn(int column) {
- mMaxPopupColumn = column;
- }
-
- public List<Key> getShiftKeys() {
- return mShiftKeys;
- }
-
- public Map<Key, Drawable> getShiftedIcons() {
- return mShiftedIcons;
- }
-
- public void enableShiftLock() {
- for (final Key key : getShiftKeys()) {
- mShiftLockEnabled.add(key);
- mNormalShiftIcons.put(key, key.getIcon());
- }
- }
-
- public boolean isShiftLockEnabled(Key key) {
- return mShiftLockEnabled.contains(key);
+ public boolean hasShiftLockKey() {
+ return !mShiftLockKeys.isEmpty();
}
public boolean setShiftLocked(boolean newShiftLockState) {
- final Map<Key, Drawable> shiftedIcons = getShiftedIcons();
- for (final Key key : getShiftKeys()) {
+ for (final Key key : mShiftLockKeys) {
+ // To represent "shift locked" state. The highlight is handled by background image that
+ // might be a StateListDrawable.
key.setHighlightOn(newShiftLockState);
- key.setIcon(newShiftLockState ? shiftedIcons.get(key) : mNormalShiftIcons.get(key));
+ // To represent "shifted" state. The key might have a shifted icon.
+ if (newShiftLockState && mShiftedIcons.containsKey(key)) {
+ key.setIcon(mShiftedIcons.get(key));
+ } else {
+ key.setIcon(mUnshiftedIcons.get(key));
+ }
}
mShiftState.setShiftLocked(newShiftLockState);
return true;
@@ -307,12 +177,11 @@ public class Keyboard {
}
public boolean setShifted(boolean newShiftState) {
- final Map<Key, Drawable> shiftedIcons = getShiftedIcons();
- for (final Key key : getShiftKeys()) {
+ for (final Key key : mShiftKeys) {
if (!newShiftState && !mShiftState.isShiftLocked()) {
- key.setIcon(mNormalShiftIcons.get(key));
+ key.setIcon(mUnshiftedIcons.get(key));
} else if (newShiftState && !mShiftState.isShiftedOrShiftLocked()) {
- key.setIcon(shiftedIcons.get(key));
+ key.setIcon(mShiftedIcons.get(key));
}
}
return mShiftState.setShifted(newShiftState);
@@ -373,61 +242,4 @@ public class Keyboard {
public int[] getNearestKeys(int x, int y) {
return mProximityInfo.getNearestKeys(x, y);
}
-
- /**
- * Compute the most common key width in order to use it as proximity key detection threshold.
- *
- * @return The most common key width in the keyboard
- */
- public int getMostCommonKeyWidth() {
- if (mMostCommonKeyWidth == 0) {
- final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
- int maxCount = 0;
- int mostCommonWidth = 0;
- for (final Key key : mKeys) {
- final Integer width = key.mWidth + key.mGap;
- Integer count = histogram.get(width);
- if (count == null)
- count = 0;
- histogram.put(width, ++count);
- if (count > maxCount) {
- maxCount = count;
- mostCommonWidth = width;
- }
- }
- mMostCommonKeyWidth = mostCommonWidth;
- }
- return mMostCommonKeyWidth;
- }
-
- /**
- * Return true if spacebar needs showing preview even when "popup on keypress" is off.
- * @param keyIndex index of the pressing key
- * @return true if spacebar needs showing preview
- */
- public boolean needSpacebarPreview(int keyIndex) {
- return false;
- }
-
- private void loadKeyboard(Context context, int xmlLayoutResId) {
- try {
- KeyboardParser parser = new KeyboardParser(this, context);
- parser.parseKeyboard(xmlLayoutResId);
- // mMinWidth is the width of this keyboard which is maximum width of row.
- mMinWidth = parser.getMaxRowWidth();
- mTotalHeight = parser.getTotalHeight();
- } catch (XmlPullParserException e) {
- Log.w(TAG, "keyboard XML parse error: " + e);
- throw new IllegalArgumentException(e);
- } catch (IOException e) {
- Log.w(TAG, "keyboard XML parse error: " + e);
- throw new RuntimeException(e);
- }
- }
-
- public static void setDefaultBounds(Drawable drawable) {
- if (drawable != null)
- drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
- drawable.getIntrinsicHeight());
- }
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index 905f779c0..864091289 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -70,4 +70,10 @@ public interface KeyboardActionListener {
* Called when user released a finger outside any key.
*/
public void onCancelInput();
+
+ /**
+ * Send a non-"code input" custom request to the listener.
+ * @return true if the request has been consumed, false otherwise.
+ */
+ public boolean onCustomRequest(int requestCode);
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index b2600dd3b..d0a2f864c 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -42,8 +42,6 @@ public class KeyboardId {
public static final int F2KEY_MODE_SHORTCUT_IME = 2;
public static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3;
- private static final int MINI_KEYBOARD_ID_MARKER = -1;
-
public final Locale mLocale;
public final int mOrientation;
public final int mWidth;
@@ -55,10 +53,9 @@ public class KeyboardId {
public final boolean mHasSettingsKey;
public final int mF2KeyMode;
public final boolean mClobberSettingsKey;
- public final boolean mVoiceKeyEnabled;
- public final boolean mHasVoiceKey;
+ public final boolean mShortcutKeyEnabled;
+ public final boolean mHasShortcutKey;
public final int mImeAction;
- public final boolean mEnableShiftLock;
public final String mXmlName;
public final EditorInfo mAttribute;
@@ -67,8 +64,7 @@ public class KeyboardId {
public KeyboardId(String xmlName, int xmlId, Locale locale, int orientation, int width,
int mode, EditorInfo attribute, boolean hasSettingsKey, int f2KeyMode,
- boolean clobberSettingsKey, boolean voiceKeyEnabled, boolean hasVoiceKey,
- boolean enableShiftLock) {
+ boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) {
final int inputType = (attribute != null) ? attribute.inputType : 0;
final int imeOptions = (attribute != null) ? attribute.imeOptions : 0;
this.mLocale = locale;
@@ -85,13 +81,12 @@ public class KeyboardId {
this.mHasSettingsKey = hasSettingsKey;
this.mF2KeyMode = f2KeyMode;
this.mClobberSettingsKey = clobberSettingsKey;
- this.mVoiceKeyEnabled = voiceKeyEnabled;
- this.mHasVoiceKey = hasVoiceKey;
+ this.mShortcutKeyEnabled = shortcutKeyEnabled;
+ this.mHasShortcutKey = hasShortcutKey;
// We are interested only in {@link EditorInfo#IME_MASK_ACTION} enum value and
// {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION}.
this.mImeAction = imeOptions & (
EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION);
- this.mEnableShiftLock = enableShiftLock;
this.mXmlName = xmlName;
this.mAttribute = attribute;
@@ -107,40 +102,29 @@ public class KeyboardId {
hasSettingsKey,
f2KeyMode,
clobberSettingsKey,
- voiceKeyEnabled,
- hasVoiceKey,
+ shortcutKeyEnabled,
+ hasShortcutKey,
mImeAction,
- enableShiftLock,
});
}
- public KeyboardId cloneAsMiniKeyboard() {
- return new KeyboardId("mini popup keyboard", MINI_KEYBOARD_ID_MARKER, mLocale, mOrientation,
- mWidth, mMode, mAttribute, false, F2KEY_MODE_NONE, false, false, false, false);
- }
-
- public KeyboardId cloneWithNewLayout(String xmlName, int xmlId) {
+ public KeyboardId cloneWithNewXml(String xmlName, int xmlId) {
return new KeyboardId(xmlName, xmlId, mLocale, mOrientation, mWidth, mMode, mAttribute,
- mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mVoiceKeyEnabled, mHasVoiceKey,
- mEnableShiftLock);
+ false, F2KEY_MODE_NONE, false, false, false);
}
- public KeyboardId cloneWithNewGeometry(int width) {
+ public KeyboardId cloneWithNewGeometry(int orientation, int width) {
if (mWidth == width)
return this;
- return new KeyboardId(mXmlName, mXmlId, mLocale, mOrientation, width, mMode, mAttribute,
- mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mVoiceKeyEnabled, mHasVoiceKey,
- mEnableShiftLock);
+ return new KeyboardId(mXmlName, mXmlId, mLocale, orientation, width, mMode, mAttribute,
+ mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mShortcutKeyEnabled,
+ mHasShortcutKey);
}
public int getXmlId() {
return mXmlId;
}
- public boolean isMiniKeyboard() {
- return mXmlId == MINI_KEYBOARD_ID_MARKER;
- }
-
public boolean isAlphabetKeyboard() {
return mXmlId == R.xml.kbd_qwerty;
}
@@ -153,8 +137,8 @@ public class KeyboardId {
return mMode == MODE_PHONE;
}
- public boolean isPhoneSymbolsKeyboard() {
- return mXmlId == R.xml.kbd_phone_symbols;
+ public boolean isPhoneShiftKeyboard() {
+ return mXmlId == R.xml.kbd_phone_shift;
}
public boolean isNumberKeyboard() {
@@ -166,7 +150,7 @@ public class KeyboardId {
return other instanceof KeyboardId && equals((KeyboardId) other);
}
- boolean equals(KeyboardId other) {
+ private boolean equals(KeyboardId other) {
return other.mLocale.equals(this.mLocale)
&& other.mOrientation == this.mOrientation
&& other.mWidth == this.mWidth
@@ -177,10 +161,9 @@ public class KeyboardId {
&& other.mHasSettingsKey == this.mHasSettingsKey
&& other.mF2KeyMode == this.mF2KeyMode
&& other.mClobberSettingsKey == this.mClobberSettingsKey
- && other.mVoiceKeyEnabled == this.mVoiceKeyEnabled
- && other.mHasVoiceKey == this.mHasVoiceKey
- && other.mImeAction == this.mImeAction
- && other.mEnableShiftLock == this.mEnableShiftLock;
+ && other.mShortcutKeyEnabled == this.mShortcutKeyEnabled
+ && other.mHasShortcutKey == this.mHasShortcutKey
+ && other.mImeAction == this.mImeAction;
}
@Override
@@ -190,7 +173,7 @@ public class KeyboardId {
@Override
public String toString() {
- return String.format("[%s.xml %s %s%d %s %s %s%s%s%s%s%s%s%s]",
+ return String.format("[%s.xml %s %s%d %s %s %s%s%s%s%s%s%s]",
mXmlName,
mLocale,
(mOrientation == 1 ? "port" : "land"), mWidth,
@@ -201,9 +184,8 @@ public class KeyboardId {
(mNavigateAction ? " navigateAction" : ""),
(mPasswordInput ? " passwordInput" : ""),
(mHasSettingsKey ? " hasSettingsKey" : ""),
- (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""),
- (mHasVoiceKey ? " hasVoiceKey" : ""),
- (mEnableShiftLock ? " enableShiftLock" : "")
+ (mShortcutKeyEnabled ? " shortcutKeyEnabled" : ""),
+ (mHasShortcutKey ? " hasShortcutKey" : "")
);
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index bb21d7a63..811470c26 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -18,7 +18,9 @@ package com.android.inputmethod.keyboard;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.content.res.Resources;
+import android.inputmethodservice.InputMethodService;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.InflateException;
@@ -27,7 +29,6 @@ import android.view.View;
import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
-import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
import com.android.inputmethod.keyboard.internal.ModifierKeyState;
import com.android.inputmethod.keyboard.internal.ShiftKeyState;
import com.android.inputmethod.latin.LatinIME;
@@ -38,6 +39,7 @@ import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.Utils;
import java.lang.ref.SoftReference;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
@@ -62,25 +64,29 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
private View mCurrentInputView;
private LatinKeyboardView mKeyboardView;
private LatinIME mInputMethodService;
+ private String mPackageName;
+ private Resources mResources;
// TODO: Combine these key state objects with auto mode switch state.
private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
- private KeyboardId mSymbolsId;
- private KeyboardId mSymbolsShiftedId;
+ private KeyboardId mMainKeyboardId;
+ private KeyboardId mSymbolsKeyboardId;
+ private KeyboardId mSymbolsShiftedKeyboardId;
private KeyboardId mCurrentId;
private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
+ // TODO: Remove this cache object when {@link DisplayMetrics} has actual window width excluding
+ // system navigation bar.
+ private WindowWidthCache mWindowWidthCache;
+
+ private KeyboardLayoutState mSavedKeyboardState = new KeyboardLayoutState();
- private EditorInfo mAttribute;
- private boolean mIsSymbols;
/** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
* what user actually typed. */
private boolean mIsAutoCorrectionActive;
- private boolean mVoiceKeyEnabled;
- private boolean mVoiceButtonOnPrimary;
// TODO: Encapsulate these state handling to separate class and combine with ShiftKeyState
// and ModifierKeyState.
@@ -94,22 +100,135 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
private static final int SWITCH_STATE_CHORDING_SYMBOL = 6;
private int mSwitchState = SWITCH_STATE_ALPHA;
- // Indicates whether or not we have the settings key in option of settings
- private boolean mSettingsKeyEnabledInSettings;
- private static final int SETTINGS_KEY_MODE_AUTO = R.string.settings_key_mode_auto;
- private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW =
- R.string.settings_key_mode_always_show;
- // NOTE: No need to have SETTINGS_KEY_MODE_ALWAYS_HIDE here because it's not being referred to
- // in the source code now.
- // Default is SETTINGS_KEY_MODE_AUTO.
- private static final int DEFAULT_SETTINGS_KEY_MODE = SETTINGS_KEY_MODE_AUTO;
-
private int mThemeIndex = -1;
private Context mThemeContext;
- private int mKeyboardWidth;
private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
+ private static class WindowWidthCache {
+ private final InputMethodService mService;
+ private final Resources mResources;
+ private final boolean mIsRegistered[] = new boolean[Configuration.ORIENTATION_SQUARE + 1];
+ private final int mWidth[] = new int[Configuration.ORIENTATION_SQUARE + 1];
+
+ public WindowWidthCache(InputMethodService service) {
+ mService = service;
+ mResources = service.getResources();
+
+ Arrays.fill(mIsRegistered, false);
+ Arrays.fill(mWidth, 0);
+ }
+
+ private int getCurrentWindowWidth() {
+ return mService.getWindow().getWindow().getDecorView().getWidth();
+ }
+
+ public int getWidth(Configuration conf) {
+ final int orientation = conf.orientation;
+ try {
+ final int width = mWidth[orientation];
+ if (mIsRegistered[orientation] || width > 0) {
+ // Return registered or cached window width for this orientation.
+ return width;
+ }
+ // Fall through
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, "unknwon orientation value " + orientation);
+ // Fall through
+ }
+
+ // Return screen width as default window width.
+ return mResources.getDisplayMetrics().widthPixels;
+ }
+
+ public int getWidthOnSizeChanged(Configuration conf) {
+ final int orientation = conf.orientation;
+ try {
+ if (mIsRegistered[orientation]) {
+ // Return registered window width for this orientation.
+ return mWidth[orientation];
+ }
+
+ // Cache the current window width without registering.
+ final int width = getCurrentWindowWidth();
+ mWidth[orientation] = width;
+ return width;
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, "unknwon orientation value " + orientation);
+ return 0;
+ }
+ }
+
+ public void registerWidth() {
+ final int orientation = mResources.getConfiguration().orientation;
+ try {
+ if (!mIsRegistered[orientation]) {
+ final int width = getCurrentWindowWidth();
+ if (width > 0) {
+ // Register current window width.
+ mWidth[orientation] = width;
+ mIsRegistered[orientation] = true;
+ }
+ }
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, "unknwon orientation value " + orientation);
+ }
+ }
+ }
+
+ public class KeyboardLayoutState {
+ private boolean mIsValid;
+ private boolean mIsAlphabetMode;
+ private boolean mIsShiftLocked;
+ private boolean mIsShifted;
+
+ public boolean isValid() {
+ return mIsValid;
+ }
+
+ public void save() {
+ if (mCurrentId == null) {
+ return;
+ }
+ mIsAlphabetMode = isAlphabetMode();
+ if (mIsAlphabetMode) {
+ mIsShiftLocked = isShiftLocked();
+ mIsShifted = !mIsShiftLocked && isShiftedOrShiftLocked();
+ } else {
+ mIsShiftLocked = false;
+ mIsShifted = mCurrentId.equals(mSymbolsShiftedKeyboardId);
+ }
+ mIsValid = true;
+ }
+
+ public KeyboardId getKeyboardId() {
+ if (!mIsValid) return mMainKeyboardId;
+
+ if (mIsAlphabetMode) {
+ return mMainKeyboardId;
+ } else {
+ return mIsShifted ? mSymbolsShiftedKeyboardId : mSymbolsKeyboardId;
+ }
+ }
+
+ public void restore() {
+ if (!mIsValid) return;
+ mIsValid = false;
+
+ if (mIsAlphabetMode) {
+ final boolean isAlphabetMode = isAlphabetMode();
+ final boolean isShiftLocked = isAlphabetMode && isShiftLocked();
+ final boolean isShifted = !isShiftLocked && isShiftedOrShiftLocked();
+ if (mIsShiftLocked != isShiftLocked) {
+ toggleCapsLock();
+ } else if (mIsShifted != isShifted) {
+ onPressShift(false);
+ onReleaseShift(false);
+ }
+ }
+ }
+ }
+
public static KeyboardSwitcher getInstance() {
return sInstance;
}
@@ -119,11 +238,18 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
}
public static void init(LatinIME ims, SharedPreferences prefs) {
- sInstance.mInputMethodService = ims;
- sInstance.mPrefs = prefs;
- sInstance.mSubtypeSwitcher = SubtypeSwitcher.getInstance();
- sInstance.setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs));
- prefs.registerOnSharedPreferenceChangeListener(sInstance);
+ sInstance.initInternal(ims, prefs);
+ }
+
+ private void initInternal(LatinIME ims, SharedPreferences prefs) {
+ mInputMethodService = ims;
+ mPackageName = ims.getPackageName();
+ mResources = ims.getResources();
+ mPrefs = prefs;
+ mSubtypeSwitcher = SubtypeSwitcher.getInstance();
+ mWindowWidthCache = new WindowWidthCache(ims);
+ setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs));
+ prefs.registerOnSharedPreferenceChangeListener(this);
}
private static int getKeyboardThemeIndex(Context context, SharedPreferences prefs) {
@@ -148,162 +274,153 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
}
}
- public void loadKeyboard(EditorInfo attribute, boolean voiceKeyEnabled,
- boolean voiceButtonOnPrimary) {
- mSwitchState = SWITCH_STATE_ALPHA;
+ public void loadKeyboard(EditorInfo editorInfo, Settings.Values settingsValues) {
try {
- final boolean isSymbols = (mCurrentId != null) ? mCurrentId.isSymbolsKeyboard() : false;
- loadKeyboardInternal(attribute, voiceKeyEnabled, voiceButtonOnPrimary, isSymbols);
+ mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues);
+ mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues);
+ mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues);
+ setKeyboard(getKeyboard(mSavedKeyboardState.getKeyboardId()));
+ updateShiftState();
} catch (RuntimeException e) {
- // Get KeyboardId to record which keyboard has been failed to load.
- final KeyboardId id = getKeyboardId(attribute, false);
- Log.w(TAG, "loading keyboard failed: " + id, e);
- LatinImeLogger.logOnException(id.toString(), e);
+ Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
+ LatinImeLogger.logOnException(mMainKeyboardId.toString(), e);
}
}
- private void loadKeyboardInternal(EditorInfo attribute, boolean voiceButtonEnabled,
- boolean voiceButtonOnPrimary, boolean isSymbols) {
- if (mKeyboardView == null) return;
+ public KeyboardLayoutState getKeyboardState() {
+ return mSavedKeyboardState;
+ }
- mAttribute = attribute;
- mVoiceKeyEnabled = voiceButtonEnabled;
- mVoiceButtonOnPrimary = voiceButtonOnPrimary;
- mIsSymbols = isSymbols;
- // Update the settings key state because number of enabled IMEs could have been changed
- mSettingsKeyEnabledInSettings = getSettingsKeyMode(mPrefs, mInputMethodService);
- final KeyboardId id = getKeyboardId(attribute, isSymbols);
+ public void onFinishInputView() {
+ mIsAutoCorrectionActive = false;
+ }
- // Note: This comment is only applied for phone number keyboard layout.
- // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch
- // between "phone keyboard" and "phone symbols keyboard". But on xlarge device,
- // "@integer/key_shift" key code is used for that purpose in order to properly display
- // "more" and "locked more" key labels. To achieve these behavior, we should initialize
- // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard"
- // respectively here for xlarge device's layout switching.
- mSymbolsId = makeSiblingKeyboardId(id, R.xml.kbd_symbols, R.xml.kbd_phone);
- mSymbolsShiftedId = makeSiblingKeyboardId(
- id, R.xml.kbd_symbols_shift, R.xml.kbd_phone_symbols);
+ public void onHideWindow() {
+ mIsAutoCorrectionActive = false;
+ }
- setKeyboard(getKeyboard(id));
+ public void registerWindowWidth() {
+ mWindowWidthCache.registerWidth();
}
- public void onSizeChanged() {
- final int width = mInputMethodService.getWindow().getWindow().getDecorView().getWidth();
+ @SuppressWarnings("unused")
+ public void onSizeChanged(int w, int h, int oldw, int oldh) {
+ // TODO: This hack should be removed when display metric returns a proper width.
+ // Until then, the behavior of KeyboardSwitcher is suboptimal on a device that has a
+ // vertical system navigation bar in landscape screen orientation, for instance.
+ final Configuration conf = mResources.getConfiguration();
+ final int width = mWindowWidthCache.getWidthOnSizeChanged(conf);
+ // If the window width hasn't fixed yet or keyboard doesn't exist, nothing to do with.
if (width == 0 || mCurrentId == null)
return;
- mKeyboardWidth = width;
- // Set keyboard with new width.
- final KeyboardId newId = mCurrentId.cloneWithNewGeometry(width);
+ // Reload keyboard with new width.
+ final KeyboardId newId = mCurrentId.cloneWithNewGeometry(conf.orientation, width);
+ mInputMethodService.mHandler.postRestoreKeyboardLayout();
setKeyboard(getKeyboard(newId));
}
- private void setKeyboard(final Keyboard newKeyboard) {
+ private void setKeyboard(final Keyboard keyboard) {
final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
- mKeyboardView.setKeyboard(newKeyboard);
- mCurrentId = newKeyboard.mId;
- final Resources res = mInputMethodService.getResources();
+ mKeyboardView.setKeyboard(keyboard);
+ mCurrentId = keyboard.mId;
+ mSwitchState = getSwitchState(mCurrentId);
+ updateShiftLockState(keyboard);
mKeyboardView.setKeyPreviewPopupEnabled(
- Settings.Values.isKeyPreviewPopupEnabled(mPrefs, res),
- Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, res));
+ Settings.Values.isKeyPreviewPopupEnabled(mPrefs, mResources),
+ Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, mResources));
final boolean localeChanged = (oldKeyboard == null)
- || !newKeyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
+ || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged);
}
+ private int getSwitchState(KeyboardId id) {
+ return id.equals(mMainKeyboardId) ? SWITCH_STATE_ALPHA : SWITCH_STATE_SYMBOL_BEGIN;
+ }
+
+ private void updateShiftLockState(Keyboard keyboard) {
+ if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
+ // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a.
+ // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
+ // that takes care of the current keyboard having such ALT key or not.
+ keyboard.setShiftLocked(keyboard.hasShiftLockKey());
+ } else if (mCurrentId.equals(mSymbolsKeyboardId)) {
+ // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
+ // indicator, we need to call setShiftLocked(false).
+ keyboard.setShiftLocked(false);
+ }
+ }
+
private LatinKeyboard getKeyboard(KeyboardId id) {
final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
LatinKeyboard keyboard = (ref == null) ? null : ref.get();
if (keyboard == null) {
- final Resources res = mInputMethodService.getResources();
- final Locale savedLocale = Utils.setSystemLocale(res,
- mSubtypeSwitcher.getInputLocale());
-
- keyboard = new LatinKeyboard(mThemeContext, id, id.mWidth);
-
- if (id.mEnableShiftLock) {
- keyboard.enableShiftLock();
+ final Locale savedLocale = Utils.setSystemLocale(
+ mResources, mSubtypeSwitcher.getInputLocale());
+ try {
+ keyboard = new LatinKeyboard.Builder(mThemeContext).load(id).build();
+ } finally {
+ Utils.setSystemLocale(mResources, savedLocale);
}
-
mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
- if (DEBUG_CACHE)
+
+ if (DEBUG_CACHE) {
Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
+ ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
-
- Utils.setSystemLocale(res, savedLocale);
+ }
} else if (DEBUG_CACHE) {
Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT id=" + id);
}
keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive);
+ keyboard.setShiftLocked(false);
keyboard.setShifted(false);
// If the cached keyboard had been switched to another keyboard while the language was
// displayed on its spacebar, it might have had arbitrary text fade factor. In such case,
// we should reset the text fade factor. It is also applicable to shortcut key.
keyboard.setSpacebarTextFadeFactor(0.0f, null);
keyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady(), null);
- keyboard.setSpacebarSlidingLanguageSwitchDiff(0);
return keyboard;
}
- private boolean hasVoiceKey(boolean isSymbols) {
- return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary);
- }
-
- private boolean hasSettingsKey(EditorInfo attribute) {
- return mSettingsKeyEnabledInSettings
- && !Utils.inPrivateImeOptions(mInputMethodService.getPackageName(),
- LatinIME.IME_OPTION_NO_SETTINGS_KEY, attribute);
- }
-
- private KeyboardId getKeyboardId(EditorInfo attribute, boolean isSymbols) {
- final int mode = Utils.getKeyboardMode(attribute);
- final boolean hasVoiceKey = hasVoiceKey(isSymbols);
+ private KeyboardId getKeyboardId(EditorInfo editorInfo, final boolean isSymbols,
+ final boolean isShift, Settings.Values settingsValues) {
+ final int mode = Utils.getKeyboardMode(editorInfo);
final int xmlId;
- final boolean enableShiftLock;
-
- if (isSymbols) {
- if (mode == KeyboardId.MODE_PHONE) {
- xmlId = R.xml.kbd_phone_symbols;
- } else if (mode == KeyboardId.MODE_NUMBER) {
- // Note: MODE_NUMBER keyboard layout has no "switch alpha symbol" key.
- xmlId = R.xml.kbd_number;
- } else {
- xmlId = R.xml.kbd_symbols;
- }
- enableShiftLock = false;
- } else {
- if (mode == KeyboardId.MODE_PHONE) {
- xmlId = R.xml.kbd_phone;
- enableShiftLock = false;
- } else if (mode == KeyboardId.MODE_NUMBER) {
- xmlId = R.xml.kbd_number;
- enableShiftLock = false;
+ switch (mode) {
+ case KeyboardId.MODE_PHONE:
+ xmlId = (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone;
+ break;
+ case KeyboardId.MODE_NUMBER:
+ xmlId = R.xml.kbd_number;
+ break;
+ default:
+ if (isSymbols) {
+ xmlId = isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols;
} else {
xmlId = R.xml.kbd_qwerty;
- enableShiftLock = true;
}
+ break;
}
- final boolean hasSettingsKey = hasSettingsKey(attribute);
- final int f2KeyMode = getF2KeyMode(mPrefs, mInputMethodService, attribute);
- final boolean clobberSettingsKey = Utils.inPrivateImeOptions(
- mInputMethodService.getPackageName(), LatinIME.IME_OPTION_NO_SETTINGS_KEY,
- attribute);
- final Resources res = mInputMethodService.getResources();
- final int orientation = res.getConfiguration().orientation;
- if (mKeyboardWidth == 0)
- mKeyboardWidth = res.getDisplayMetrics().widthPixels;
- final Locale locale = mSubtypeSwitcher.getInputLocale();
- return new KeyboardId(
- res.getResourceEntryName(xmlId), xmlId, locale, orientation, mKeyboardWidth,
- mode, attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, mVoiceKeyEnabled,
- hasVoiceKey, enableShiftLock);
- }
- private KeyboardId makeSiblingKeyboardId(KeyboardId base, int alphabet, int phone) {
- final int xmlId = base.mMode == KeyboardId.MODE_PHONE ? phone : alphabet;
- final String xmlName = mInputMethodService.getResources().getResourceEntryName(xmlId);
- return base.cloneWithNewLayout(xmlName, xmlId);
+ final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled(editorInfo);
+ final boolean noMicrophone = Utils.inPrivateImeOptions(
+ mPackageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
+ || Utils.inPrivateImeOptions(
+ null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
+ final boolean voiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo)
+ && !noMicrophone;
+ final boolean voiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
+ final boolean noSettingsKey = Utils.inPrivateImeOptions(
+ mPackageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
+ final boolean hasSettingsKey = settingsKeyEnabled && !noSettingsKey;
+ final int f2KeyMode = getF2KeyMode(settingsKeyEnabled, noSettingsKey);
+ final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != voiceKeyOnMain);
+ final Configuration conf = mResources.getConfiguration();
+
+ return new KeyboardId(
+ mResources.getResourceEntryName(xmlId), xmlId, mSubtypeSwitcher.getInputLocale(),
+ conf.orientation, mWindowWidthCache.getWidth(conf), mode, editorInfo,
+ hasSettingsKey, f2KeyMode, noSettingsKey, voiceKeyEnabled, hasShortcutKey);
}
public int getKeyboardMode() {
@@ -593,16 +710,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
if (isAlphabetMode())
return;
final LatinKeyboard keyboard;
- if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) {
- keyboard = getKeyboard(mSymbolsShiftedId);
- // Symbol shifted keyboard has an ALT key that has a caps lock style indicator. To
- // enable the indicator, we need to call setShiftLocked(true).
- keyboard.setShiftLocked(true);
+ if (mCurrentId.equals(mSymbolsKeyboardId)
+ || !mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
+ keyboard = getKeyboard(mSymbolsShiftedKeyboardId);
} else {
- keyboard = getKeyboard(mSymbolsId);
- // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
- // indicator, we need to call setShiftLocked(false).
- keyboard.setShiftLocked(false);
+ keyboard = getKeyboard(mSymbolsKeyboardId);
}
setKeyboard(keyboard);
}
@@ -621,11 +733,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
}
private void toggleKeyboardMode() {
- loadKeyboardInternal(mAttribute, mVoiceKeyEnabled, mVoiceButtonOnPrimary, !mIsSymbols);
- if (mIsSymbols) {
- mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
+ if (mCurrentId.equals(mMainKeyboardId)) {
+ setKeyboard(getKeyboard(mSymbolsKeyboardId));
} else {
- mSwitchState = SWITCH_STATE_ALPHA;
+ setKeyboard(getKeyboard(mMainKeyboardId));
}
}
@@ -675,10 +786,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
// {@link #SWITCH_STATE_MOMENTARY}.
if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
// Detected only the mode change key has been pressed, and then released.
- if (mIsSymbols) {
- mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
- } else {
+ if (mCurrentId.equals(mMainKeyboardId)) {
mSwitchState = SWITCH_STATE_ALPHA;
+ } else {
+ mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
}
} else if (getPointerCount() == 1) {
// Snap back to the previous keyboard mode if the user pressed the mode change key
@@ -788,11 +899,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (PREF_KEYBOARD_LAYOUT.equals(key)) {
- final int layoutId = getKeyboardThemeIndex(mInputMethodService, sharedPreferences);
- postSetInputView(createInputView(layoutId, false));
- } else if (Settings.PREF_SETTINGS_KEY.equals(key)) {
- mSettingsKeyEnabledInSettings = getSettingsKeyMode(sharedPreferences,
- mInputMethodService);
+ final int themeIndex = getKeyboardThemeIndex(mInputMethodService, sharedPreferences);
+ postSetInputView(createInputView(themeIndex, false));
+ } else if (Settings.PREF_SHOW_SETTINGS_KEY.equals(key)) {
postSetInputView(createInputView(mThemeIndex, true));
}
}
@@ -810,41 +919,18 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
}
}
- private static boolean getSettingsKeyMode(SharedPreferences prefs, Context context) {
- final Resources res = context.getResources();
- final boolean showSettingsKeyOption = res.getBoolean(
- R.bool.config_enable_show_settings_key_option);
- if (showSettingsKeyOption) {
- final String settingsKeyMode = prefs.getString(Settings.PREF_SETTINGS_KEY,
- res.getString(DEFAULT_SETTINGS_KEY_MODE));
- // We show the settings key when 1) SETTINGS_KEY_MODE_ALWAYS_SHOW or
- // 2) SETTINGS_KEY_MODE_AUTO and there are two or more enabled IMEs on the system
- if (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW))
- || (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_AUTO))
- && Utils.hasMultipleEnabledIMEsOrSubtypes(
- (InputMethodManagerCompatWrapper.getInstance(context))))) {
- return true;
- }
- return false;
- }
- // If the show settings key option is disabled, we always try showing the settings key.
- return true;
- }
-
- private static int getF2KeyMode(SharedPreferences prefs, Context context,
- EditorInfo attribute) {
- final boolean clobberSettingsKey = Utils.inPrivateImeOptions(
- context.getPackageName(), LatinIME.IME_OPTION_NO_SETTINGS_KEY, attribute);
- final Resources res = context.getResources();
- final String settingsKeyMode = prefs.getString(Settings.PREF_SETTINGS_KEY,
- res.getString(DEFAULT_SETTINGS_KEY_MODE));
- if (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_AUTO))) {
- return clobberSettingsKey ? KeyboardId.F2KEY_MODE_SHORTCUT_IME
- : KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
- } else if (settingsKeyMode.equals(res.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW))) {
- return clobberSettingsKey ? KeyboardId.F2KEY_MODE_NONE : KeyboardId.F2KEY_MODE_SETTINGS;
- } else { // SETTINGS_KEY_MODE_ALWAYS_HIDE
+ private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) {
+ if (noSettingsKey) {
+ // Never shows the Settings key
return KeyboardId.F2KEY_MODE_SHORTCUT_IME;
}
+
+ if (settingsKeyEnabled) {
+ return KeyboardId.F2KEY_MODE_SETTINGS;
+ } else {
+ // It should be alright to fall back to the Settings key on 7-inch layouts
+ // even when the Settings key is not explicitly enabled.
+ return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
+ }
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 2e0683115..2df2994f6 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -35,6 +35,7 @@ import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.inputmethod.compat.FrameLayoutCompatUtils;
@@ -73,8 +74,6 @@ import java.util.HashMap;
* @attr ref R.styleable#KeyboardView_shadowRadius
*/
public class KeyboardView extends View implements PointerTracker.DrawingProxy {
- private static final boolean DEBUG_KEYBOARD_GRID = false;
-
// Miscellaneous constants
private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
@@ -89,25 +88,23 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private final KeyDrawParams mKeyDrawParams;
// Key preview
+ private final int mKeyPreviewLayoutId;
private final KeyPreviewDrawParams mKeyPreviewDrawParams;
- private final TextView mPreviewText;
private boolean mShowKeyPreviewPopup = true;
private final int mDelayBeforePreview;
private int mDelayAfterPreview;
private ViewGroup mPreviewPlacer;
// Drawing
- /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/
- private boolean mDrawPending;
- /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */
- private boolean mKeyboardChanged;
+ /** Whether the keyboard bitmap buffer needs to be redrawn before it's blitted. **/
+ private boolean mBufferNeedsUpdate;
/** The dirty region in the keyboard bitmap */
private final Rect mDirtyRect = new Rect();
/** The key to invalidate. */
private Key mInvalidatedKey;
/** The dirty region for single key drawing */
private final Rect mInvalidatedKeyRect = new Rect();
- /** The keyboard bitmap for faster updates */
+ /** The keyboard bitmap buffer for faster updates */
private Bitmap mBuffer;
/** The canvas for the above mutable keyboard bitmap */
private Canvas mCanvas;
@@ -136,23 +133,23 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
@Override
public void handleMessage(Message msg) {
final KeyboardView keyboardView = getOuterInstance();
+ if (keyboardView == null) return;
final PointerTracker tracker = (PointerTracker) msg.obj;
switch (msg.what) {
case MSG_SHOW_KEY_PREVIEW:
keyboardView.showKey(msg.arg1, tracker);
break;
case MSG_DISMISS_KEY_PREVIEW:
- if (keyboardView.mPreviewText != null) {
- keyboardView.mPreviewText.setVisibility(View.INVISIBLE);
- }
+ tracker.getKeyPreviewText().setVisibility(View.INVISIBLE);
break;
}
}
public void showKeyPreview(long delay, int keyIndex, PointerTracker tracker) {
- final KeyboardView keyboardView = getOuterInstance();
removeMessages(MSG_SHOW_KEY_PREVIEW);
- if (keyboardView.mPreviewText.getVisibility() == VISIBLE || delay == 0) {
+ final KeyboardView keyboardView = getOuterInstance();
+ if (keyboardView == null) return;
+ if (tracker.getKeyPreviewText().getVisibility() == VISIBLE || delay == 0) {
// Show right away, if it's already visible and finger is moving around
keyboardView.showKey(keyIndex, tracker);
} else {
@@ -265,7 +262,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
public final Drawable mPreviewBackground;
public final Drawable mPreviewLeftBackground;
public final Drawable mPreviewRightBackground;
- public final Drawable mPreviewSpacebarBackground;
public final int mPreviewTextColor;
public final int mPreviewOffset;
public final int mPreviewHeight;
@@ -286,8 +282,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
R.styleable.KeyboardView_keyPreviewLeftBackground);
mPreviewRightBackground = a.getDrawable(
R.styleable.KeyboardView_keyPreviewRightBackground);
- mPreviewSpacebarBackground = a.getDrawable(
- R.styleable.KeyboardView_keyPreviewSpacebarBackground);
setAlpha(mPreviewBackground, PREVIEW_ALPHA);
setAlpha(mPreviewLeftBackground, PREVIEW_ALPHA);
setAlpha(mPreviewRightBackground, PREVIEW_ALPHA);
@@ -326,11 +320,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mKeyDrawParams = new KeyDrawParams(a);
mKeyPreviewDrawParams = new KeyPreviewDrawParams(a, mKeyDrawParams);
- final int previewLayout = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0);
- if (previewLayout != 0) {
- mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null);
- } else {
- mPreviewText = null;
+ mKeyPreviewLayoutId = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0);
+ if (mKeyPreviewLayoutId == 0) {
mShowKeyPreviewPopup = false;
}
mBackgroundDimAmount = a.getFloat(R.styleable.KeyboardView_backgroundDimAmount, 0.5f);
@@ -359,14 +350,18 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
* @param keyboard the keyboard to display in this view
*/
public void setKeyboard(Keyboard keyboard) {
- // Remove any pending messages, except dismissing preview
+ // Remove any pending dismissing preview
mDrawingHandler.cancelAllShowKeyPreviews();
+ if (mKeyboard != null) {
+ PointerTracker.dismissAllKeyPreviews();
+ }
mKeyboard = keyboard;
LatinImeLogger.onSetKeyboard(keyboard);
requestLayout();
- mKeyboardChanged = true;
+ mDirtyRect.set(0, 0, getWidth(), getHeight());
+ mBufferNeedsUpdate = true;
invalidateAllKeys();
- final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap();
+ final int keyHeight = keyboard.mDefaultRowHeight - keyboard.mVerticalGap;
mKeyDrawParams.updateKeyHeight(keyHeight);
mKeyPreviewDrawParams.updateKeyHeight(keyHeight);
}
@@ -402,25 +397,21 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
@Override
- public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // Round up a little
- if (mKeyboard == null) {
- setMeasuredDimension(
- getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom());
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mKeyboard != null) {
+ // The main keyboard expands to the display width.
+ final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
+ setMeasuredDimension(widthMeasureSpec, height);
} else {
- int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight();
- if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) {
- width = MeasureSpec.getSize(widthMeasureSpec);
- }
- setMeasuredDimension(
- width, mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom());
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (mDrawPending || mBuffer == null || mKeyboardChanged) {
+ if (mBufferNeedsUpdate || mBuffer == null) {
+ mBufferNeedsUpdate = false;
onBufferDraw();
}
canvas.drawBitmap(mBuffer, 0, 0, null);
@@ -431,14 +422,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
final int height = getHeight();
if (width == 0 || height == 0)
return;
- if (mBuffer == null || mKeyboardChanged) {
- mKeyboardChanged = false;
- mDirtyRect.union(0, 0, width, height);
- }
if (mBuffer == null || mBuffer.getWidth() != width || mBuffer.getHeight() != height) {
if (mBuffer != null)
mBuffer.recycle();
mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ mDirtyRect.union(0, 0, width, height);
if (mCanvas != null) {
mCanvas.setBitmap(mBuffer);
} else {
@@ -459,37 +447,20 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
+ getPaddingLeft();
final int keyDrawY = mInvalidatedKey.mY + getPaddingTop();
canvas.translate(keyDrawX, keyDrawY);
- onBufferDrawKey(mInvalidatedKey, canvas, mPaint, params, isManualTemporaryUpperCase);
+ onBufferDrawKey(mInvalidatedKey, mKeyboard, canvas, mPaint, params,
+ isManualTemporaryUpperCase);
canvas.translate(-keyDrawX, -keyDrawY);
} else {
// Draw all keys.
- for (final Key key : mKeyboard.getKeys()) {
+ for (final Key key : mKeyboard.mKeys) {
final int keyDrawX = key.mX + key.mVisualInsetsLeft + getPaddingLeft();
final int keyDrawY = key.mY + getPaddingTop();
canvas.translate(keyDrawX, keyDrawY);
- onBufferDrawKey(key, canvas, mPaint, params, isManualTemporaryUpperCase);
+ onBufferDrawKey(key, mKeyboard, canvas, mPaint, params, isManualTemporaryUpperCase);
canvas.translate(-keyDrawX, -keyDrawY);
}
}
- // TODO: Move this function to ProximityInfo for getting rid of
- // public declarations for
- // GRID_WIDTH and GRID_HEIGHT
- if (DEBUG_KEYBOARD_GRID) {
- Paint p = new Paint();
- p.setStyle(Paint.Style.STROKE);
- p.setStrokeWidth(1.0f);
- p.setColor(0x800000c0);
- int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1)
- / mKeyboard.GRID_WIDTH;
- int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1)
- / mKeyboard.GRID_HEIGHT;
- for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++)
- canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p);
- for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++)
- canvas.drawLine(0, i * ch, cw * mKeyboard.GRID_WIDTH, i * ch, p);
- }
-
// Overlay a dark rectangle to dim the keyboard
if (needsToDimKeyboard()) {
mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
@@ -497,7 +468,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
mInvalidatedKey = null;
- mDrawPending = false;
mDirtyRect.setEmpty();
}
@@ -505,8 +475,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
return false;
}
- private static void onBufferDrawKey(final Key key, final Canvas canvas, Paint paint,
- KeyDrawParams params, boolean isManualTemporaryUpperCase) {
+ private static void onBufferDrawKey(final Key key, final Keyboard keyboard, final Canvas canvas,
+ Paint paint, KeyDrawParams params, boolean isManualTemporaryUpperCase) {
final boolean debugShowAlign = LatinImeLogger.sVISUALDEBUG;
// Draw key background.
final int bgWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight
@@ -529,7 +499,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
canvas.translate(-bgX, -bgY);
// Draw key top visuals.
- final int keyWidth = key.mWidth;
+ final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
final int keyHeight = key.mHeight;
final float centerX = keyWidth * 0.5f;
final float centerY = keyHeight * 0.5f;
@@ -539,10 +509,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
// Draw key label.
+ final Drawable icon = key.getIcon();
float positionX = centerX;
if (key.mLabel != null) {
// Switch the character to uppercase if shift is pressed
- final CharSequence label = key.getCaseAdjustedLabel();
+ final CharSequence label = keyboard.adjustLabelCase(key.mLabel);
// For characters, use large font. For labels like "Done", use smaller font.
paint.setTypeface(key.selectTypeface(params.mKeyTextStyle));
final int labelSize = key.selectTextSize(params.mKeyLetterSize,
@@ -555,16 +526,25 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
final float baseline = centerY + labelCharHeight / 2;
// Horizontal label text alignment
- if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT) != 0) {
+ float labelWidth = 0;
+ if (key.isAlignLeft()) {
positionX = (int)params.mKeyLabelHorizontalPadding;
paint.setTextAlign(Align.LEFT);
- } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_RIGHT) != 0) {
+ } else if (key.isAlignRight()) {
positionX = keyWidth - (int)params.mKeyLabelHorizontalPadding;
paint.setTextAlign(Align.RIGHT);
- } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0) {
+ } else if (key.isAlignLeftOfCenter()) {
// TODO: Parameterise this?
positionX = centerX - labelCharWidth * 7 / 4;
paint.setTextAlign(Align.LEFT);
+ } else if (key.hasLabelWithIconLeft() && icon != null) {
+ labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth();
+ positionX = centerX + labelWidth / 2;
+ paint.setTextAlign(Align.RIGHT);
+ } else if (key.hasLabelWithIconRight() && icon != null) {
+ labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth();
+ positionX = centerX - labelWidth / 2;
+ paint.setTextAlign(Align.LEFT);
} else {
positionX = centerX;
paint.setTextAlign(Align.CENTER);
@@ -586,6 +566,19 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// Turn off drop shadow
paint.setShadowLayer(0, 0, 0, 0);
+ if (icon != null) {
+ final int iconWidth = icon.getIntrinsicWidth();
+ final int iconHeight = icon.getIntrinsicHeight();
+ final int iconY = (keyHeight - iconHeight) / 2;
+ if (key.hasLabelWithIconLeft()) {
+ final int iconX = (int)(centerX - labelWidth / 2);
+ drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
+ } else if (key.hasLabelWithIconRight()) {
+ final int iconX = (int)(centerX + labelWidth / 2 - iconWidth);
+ drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
+ }
+ }
+
if (debugShowAlign) {
final Paint line = new Paint();
drawHorizontalLine(canvas, baseline, keyWidth, 0xc0008000, line);
@@ -639,16 +632,15 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
// Draw key icon.
- final Drawable icon = key.getIcon();
if (key.mLabel == null && icon != null) {
final int iconWidth = icon.getIntrinsicWidth();
final int iconHeight = icon.getIntrinsicHeight();
final int iconX, alignX;
final int iconY = (keyHeight - iconHeight) / 2;
- if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT) != 0) {
+ if (key.isAlignLeft()) {
iconX = (int)params.mKeyLabelHorizontalPadding;
alignX = iconX;
- } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_RIGHT) != 0) {
+ } else if (key.isAlignRight()) {
iconX = keyWidth - (int)params.mKeyLabelHorizontalPadding - iconWidth;
alignX = iconX + iconWidth;
} else { // Align center
@@ -665,7 +657,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
// Draw popup hint "..." at the bottom right corner of the key.
- if (key.hasPopupHint()) {
+ if ((key.hasPopupHint() && key.mPopupCharacters != null && key.mPopupCharacters.length > 0)
+ || key.needsSpecialPopupHint()) {
paint.setTextSize(params.mKeyHintLetterSize);
paint.setColor(params.mKeyHintLabelColor);
paint.setTextAlign(Align.CENTER);
@@ -728,6 +721,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
return width;
}
+ private static float getLabelWidth(CharSequence label, Paint paint) {
+ paint.getTextBounds(label.toString(), 0, label.length(), sTextBounds);
+ return sTextBounds.width();
+ }
+
private static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width,
int height) {
canvas.translate(x, y);
@@ -764,13 +762,21 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mDrawingHandler.cancelAllMessages();
}
+ // Called by {@link PointerTracker} constructor to create a TextView.
+ @Override
+ public TextView inflateKeyPreviewText() {
+ final Context context = getContext();
+ if (mKeyPreviewLayoutId != 0) {
+ return (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null);
+ } else {
+ return new TextView(context);
+ }
+ }
+
@Override
public void showKeyPreview(int keyIndex, PointerTracker tracker) {
if (mShowKeyPreviewPopup) {
mDrawingHandler.showKeyPreview(mDelayBeforePreview, keyIndex, tracker);
- } else if (mKeyboard.needSpacebarPreview(keyIndex)) {
- // Show key preview (in this case, slide language switcher) without any delay.
- showKey(keyIndex, tracker);
}
}
@@ -781,34 +787,30 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
@Override
public void dismissKeyPreview(PointerTracker tracker) {
- if (mShowKeyPreviewPopup) {
- mDrawingHandler.cancelShowKeyPreview(tracker);
- mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
- } else if (mKeyboard.needSpacebarPreview(KeyDetector.NOT_A_KEY)) {
- // Dismiss key preview (in this case, slide language switcher) without any delay.
- mPreviewText.setVisibility(View.INVISIBLE);
- }
+ mDrawingHandler.cancelShowKeyPreview(tracker);
+ mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
}
private void addKeyPreview(TextView keyPreview) {
if (mPreviewPlacer == null) {
- mPreviewPlacer = FrameLayoutCompatUtils.getPlacer(
- (ViewGroup)getRootView().findViewById(android.R.id.content));
+ mPreviewPlacer = new RelativeLayout(getContext());
+ final ViewGroup windowContentView =
+ (ViewGroup)getRootView().findViewById(android.R.id.content);
+ windowContentView.addView(mPreviewPlacer);
}
- final ViewGroup placer = mPreviewPlacer;
- placer.addView(keyPreview, FrameLayoutCompatUtils.newLayoutParam(placer, 0, 0));
+ mPreviewPlacer.addView(
+ keyPreview, FrameLayoutCompatUtils.newLayoutParam(mPreviewPlacer, 0, 0));
}
- // TODO: Introduce minimum duration for displaying key previews
- // TODO: Display up to two key previews when the user presses two keys at the same time
private void showKey(final int keyIndex, PointerTracker tracker) {
- final TextView previewText = mPreviewText;
+ final TextView previewText = tracker.getKeyPreviewText();
// If the key preview has no parent view yet, add it to the ViewGroup which can place
// key preview absolutely in SoftInputWindow.
if (previewText.getParent() == null) {
addKeyPreview(previewText);
}
+ mDrawingHandler.cancelDismissKeyPreview(tracker);
final Key key = tracker.getKey(keyIndex);
// If keyIndex is invalid or IME is already closed, we must not show key preview.
// Trying to show key preview while root window is closed causes
@@ -816,7 +818,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
if (key == null)
return;
- mDrawingHandler.cancelAllDismissKeyPreviews();
final KeyPreviewDrawParams params = mKeyPreviewDrawParams;
final int keyDrawX = key.mX + key.mVisualInsetsLeft;
final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
@@ -831,18 +832,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mPreviewTextSize);
previewText.setTypeface(params.mKeyTextStyle);
}
- previewText.setText(key.getCaseAdjustedLabel());
+ previewText.setText(mKeyboard.adjustLabelCase(key.mLabel));
} else {
final Drawable previewIcon = key.getPreviewIcon();
previewText.setCompoundDrawables(null, null, null,
previewIcon != null ? previewIcon : key.getIcon());
previewText.setText(null);
}
- if (key.mCode == Keyboard.CODE_SPACE) {
- previewText.setBackgroundDrawable(params.mPreviewSpacebarBackground);
- } else {
- previewText.setBackgroundDrawable(params.mPreviewBackground);
- }
+ previewText.setBackgroundDrawable(params.mPreviewBackground);
previewText.measure(MEASURESPEC_UNSPECIFIED, MEASURESPEC_UNSPECIFIED);
final int previewWidth = Math.max(previewText.getMeasuredWidth(), keyDrawWidth
@@ -877,7 +874,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
*/
public void invalidateAllKeys() {
mDirtyRect.union(0, 0, getWidth(), getHeight());
- mDrawPending = true;
+ mBufferNeedsUpdate = true;
invalidate();
}
@@ -897,18 +894,23 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
final int y = key.mY + getPaddingTop();
mInvalidatedKeyRect.set(x, y, x + key.mWidth, y + key.mHeight);
mDirtyRect.union(mInvalidatedKeyRect);
- onBufferDraw();
+ mBufferNeedsUpdate = true;
invalidate(mInvalidatedKeyRect);
}
public void closing() {
- mPreviewText.setVisibility(View.GONE);
+ PointerTracker.dismissAllKeyPreviews();
cancelAllMessages();
mDirtyRect.union(0, 0, getWidth(), getHeight());
requestLayout();
}
+ @Override
+ public boolean dismissPopupPanel() {
+ return false;
+ }
+
public void purgeKeyboardAndClosing() {
mKeyboard = null;
closing();
@@ -918,5 +920,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
closing();
+ if (mPreviewPlacer != null) {
+ mPreviewPlacer.removeAllViews();
+ }
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index d925b8c33..1b6f57b92 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -31,23 +31,22 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
-import com.android.inputmethod.keyboard.internal.SlidingLocaleDrawable;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.List;
import java.util.Locale;
// TODO: We should remove this class
public class LatinKeyboard extends Keyboard {
private static final int SPACE_LED_LENGTH_PERCENT = 80;
- public static final int CODE_NEXT_LANGUAGE = -100;
- public static final int CODE_PREV_LANGUAGE = -101;
-
private final Resources mRes;
private final Theme mTheme;
private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance();
@@ -55,34 +54,20 @@ public class LatinKeyboard extends Keyboard {
/* Space key and its icons, drawables and colors. */
private final Key mSpaceKey;
private final Drawable mSpaceIcon;
- private final Drawable mSpacePreviewIcon;
- private final int mSpaceKeyIndex;
private final boolean mAutoCorrectionSpacebarLedEnabled;
private final Drawable mAutoCorrectionSpacebarLedIcon;
private final int mSpacebarTextColor;
private final int mSpacebarTextShadowColor;
private float mSpacebarTextFadeFactor = 0.0f;
- private final int mSpacebarLanguageSwitchThreshold;
- private int mSpacebarSlidingLanguageSwitchDiff;
- private final SlidingLocaleDrawable mSlidingLocaleIcon;
private final HashMap<Integer, SoftReference<BitmapDrawable>> mSpaceDrawableCache =
new HashMap<Integer, SoftReference<BitmapDrawable>>();
+ private final boolean mIsSpacebarTriggeringPopupByLongPress;
/* Shortcut key and its icons if available */
private final Key mShortcutKey;
private final Drawable mEnabledShortcutIcon;
private final Drawable mDisabledShortcutIcon;
- // BLACK LEFT-POINTING TRIANGLE and two spaces.
- public static final String ARROW_LEFT = "\u25C0 ";
- // Two spaces and BLACK RIGHT-POINTING TRIANGLE.
- public static final String ARROW_RIGHT = " \u25B6";
-
- // Minimum width of spacebar dragging to trigger the language switch (represented by the number
- // of the most common key width of this keyboard).
- private static final int SPACEBAR_DRAG_WIDTH = 3;
- // Minimum width of space key preview (proportional to keyboard width).
- private static final float SPACEBAR_POPUP_MIN_RATIO = 0.5f;
// Height in space key the language name will be drawn. (proportional to space key height)
public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f;
// If the full language name needs to be smaller than this value to be drawn on space key,
@@ -92,35 +77,20 @@ public class LatinKeyboard extends Keyboard {
private static final String SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "small";
private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium";
- public LatinKeyboard(Context context, KeyboardId id, int width) {
- super(context, id.getXmlId(), id, width);
+ private LatinKeyboard(Context context, LatinKeyboardParams params) {
+ super(params);
mRes = context.getResources();
mTheme = context.getTheme();
- final List<Key> keys = getKeys();
- int spaceKeyIndex = -1;
- int shortcutKeyIndex = -1;
- final int keyCount = keys.size();
- for (int index = 0; index < keyCount; index++) {
- // For now, assuming there are up to one space key and one shortcut key respectively.
- switch (keys.get(index).mCode) {
- case CODE_SPACE:
- spaceKeyIndex = index;
- break;
- case CODE_SHORTCUT:
- shortcutKeyIndex = index;
- break;
- }
- }
-
// The index of space key is available only after Keyboard constructor has finished.
- mSpaceKey = (spaceKeyIndex >= 0) ? keys.get(spaceKeyIndex) : null;
+ mSpaceKey = params.mSpaceKey;
mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon() : null;
- mSpacePreviewIcon = (mSpaceKey != null) ? mSpaceKey.getPreviewIcon() : null;
- mSpaceKeyIndex = spaceKeyIndex;
- mShortcutKey = (shortcutKeyIndex >= 0) ? keys.get(shortcutKeyIndex) : null;
+ mShortcutKey = params.mShortcutKey;
mEnabledShortcutIcon = (mShortcutKey != null) ? mShortcutKey.getIcon() : null;
+ final int longPressSpaceKeyTimeout =
+ mRes.getInteger(R.integer.config_long_press_space_key_timeout);
+ mIsSpacebarTriggeringPopupByLongPress = (longPressSpaceKeyTimeout > 0);
final TypedArray a = context.obtainStyledAttributes(
null, R.styleable.LatinKeyboard, R.attr.latinKeyboardStyle, R.style.LatinKeyboard);
@@ -133,19 +103,41 @@ public class LatinKeyboard extends Keyboard {
mSpacebarTextShadowColor = a.getColor(
R.styleable.LatinKeyboard_spacebarTextShadowColor, 0);
a.recycle();
+ }
- // The threshold is "key width" x 1.25
- mSpacebarLanguageSwitchThreshold = (getMostCommonKeyWidth() * 5) / 4;
+ private static class LatinKeyboardParams extends KeyboardParams {
+ public Key mSpaceKey = null;
+ public Key mShortcutKey = null;
- if (mSpaceKey != null && mSpacePreviewIcon != null) {
- final int slidingIconWidth = Math.max(mSpaceKey.mWidth,
- (int)(getMinWidth() * SPACEBAR_POPUP_MIN_RATIO));
- final int spaceKeyheight = mSpacePreviewIcon.getIntrinsicHeight();
- mSlidingLocaleIcon = new SlidingLocaleDrawable(
- context, mSpacePreviewIcon, slidingIconWidth, spaceKeyheight);
- mSlidingLocaleIcon.setBounds(0, 0, slidingIconWidth, spaceKeyheight);
- } else {
- mSlidingLocaleIcon = null;
+ @Override
+ public void onAddKey(Key key) {
+ super.onAddKey(key);
+
+ switch (key.mCode) {
+ case Keyboard.CODE_SPACE:
+ mSpaceKey = key;
+ break;
+ case Keyboard.CODE_SHORTCUT:
+ mShortcutKey = key;
+ break;
+ }
+ }
+ }
+
+ public static class Builder extends KeyboardBuilder<LatinKeyboardParams> {
+ public Builder(Context context) {
+ super(context, new LatinKeyboardParams());
+ }
+
+ @Override
+ public Builder load(KeyboardId id) {
+ super.load(id);
+ return this;
+ }
+
+ @Override
+ public LatinKeyboard build() {
+ return new LatinKeyboard(mContext, mParams);
}
}
@@ -193,8 +185,13 @@ public class LatinKeyboard extends Keyboard {
}
private void updateSpacebarForLocale(boolean isAutoCorrection) {
- if (mSpaceKey == null)
- return;
+ if (mSpaceKey == null) return;
+ final InputMethodManagerCompatWrapper imm = InputMethodManagerCompatWrapper.getInstance();
+ if (imm == null) return;
+ // The "..." popup hint for triggering something by a long-pressing the spacebar
+ final boolean shouldShowInputMethodPicker = mIsSpacebarTriggeringPopupByLongPress
+ && Utils.hasMultipleEnabledIMEsOrSubtypes(imm, true /* include aux subtypes */);
+ mSpaceKey.setNeedsSpecialPopupHint(shouldShowInputMethodPicker);
// If application locales are explicitly selected.
if (mSubtypeSwitcher.needsToDisplayLanguage()) {
mSpaceKey.setIcon(getSpaceDrawable(
@@ -219,8 +216,7 @@ public class LatinKeyboard extends Keyboard {
final Rect bounds = new Rect();
// Estimate appropriate language name text size to fit in maxTextWidth.
- String language = ARROW_LEFT + SubtypeSwitcher.getFullDisplayName(locale, true)
- + ARROW_RIGHT;
+ String language = SubtypeSwitcher.getFullDisplayName(locale, true);
int textWidth = getTextWidth(paint, language, origTextSize, bounds);
// Assuming text width and text size are proportional to each other.
float textSize = origTextSize * Math.min(width / textWidth, 1.0f);
@@ -232,7 +228,7 @@ public class LatinKeyboard extends Keyboard {
final boolean useShortName;
if (useMiddleName) {
- language = ARROW_LEFT + SubtypeSwitcher.getMiddleDisplayLanguage(locale) + ARROW_RIGHT;
+ language = SubtypeSwitcher.getMiddleDisplayLanguage(locale);
textWidth = getTextWidth(paint, language, origTextSize, bounds);
textSize = origTextSize * Math.min(width / textWidth, 1.0f);
useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME)
@@ -242,7 +238,7 @@ public class LatinKeyboard extends Keyboard {
}
if (useShortName) {
- language = ARROW_LEFT + SubtypeSwitcher.getShortDisplayLanguage(locale) + ARROW_RIGHT;
+ language = SubtypeSwitcher.getShortDisplayLanguage(locale);
textWidth = getTextWidth(paint, language, origTextSize, bounds);
textSize = origTextSize * Math.min(width / textWidth, 1.0f);
}
@@ -327,68 +323,11 @@ public class LatinKeyboard extends Keyboard {
return buffer;
}
- public void setSpacebarSlidingLanguageSwitchDiff(int diff) {
- mSpacebarSlidingLanguageSwitchDiff = diff;
- }
-
- public void updateSpacebarPreviewIcon(int diff) {
- if (mSpacebarSlidingLanguageSwitchDiff == diff)
- return;
- mSpacebarSlidingLanguageSwitchDiff = diff;
- if (mSlidingLocaleIcon == null)
- return;
- mSlidingLocaleIcon.setDiff(diff);
- if (Math.abs(diff) == Integer.MAX_VALUE) {
- mSpaceKey.setPreviewIcon(mSpacePreviewIcon);
- } else {
- mSpaceKey.setPreviewIcon(mSlidingLocaleIcon);
- }
- mSpaceKey.getPreviewIcon().invalidateSelf();
- }
-
- public boolean shouldTriggerSpacebarSlidingLanguageSwitch(int diff) {
- // On phone and number layouts, sliding language switch is disabled.
- // TODO: Sort out how to enable language switch on these layouts.
- if (isPhoneKeyboard() || isNumberKeyboard())
- return false;
- return Math.abs(diff) > mSpacebarLanguageSwitchThreshold;
- }
-
- /**
- * Return true if spacebar needs showing preview even when "popup on keypress" is off.
- * @param keyIndex index of the pressing key
- * @return true if spacebar needs showing preview
- */
- @Override
- public boolean needSpacebarPreview(int keyIndex) {
- // This method is called when "popup on keypress" is off.
- if (!mSubtypeSwitcher.useSpacebarLanguageSwitcher())
- return false;
- // Dismiss key preview.
- if (keyIndex == KeyDetector.NOT_A_KEY)
- return true;
- // Key is not a spacebar.
- if (keyIndex != mSpaceKeyIndex)
- return false;
- // The language switcher will be displayed only when the dragging distance is greater
- // than the threshold.
- return shouldTriggerSpacebarSlidingLanguageSwitch(mSpacebarSlidingLanguageSwitchDiff);
- }
-
- public int getLanguageChangeDirection() {
- if (mSpaceKey == null || mSubtypeSwitcher.getEnabledKeyboardLocaleCount() <= 1
- || Math.abs(mSpacebarSlidingLanguageSwitchDiff)
- < getMostCommonKeyWidth() * SPACEBAR_DRAG_WIDTH) {
- return 0; // No change
- }
- return mSpacebarSlidingLanguageSwitchDiff > 0 ? 1 : -1;
- }
-
@Override
public int[] getNearestKeys(int x, int y) {
// Avoid dead pixels at edges of the keyboard
- return super.getNearestKeys(Math.max(0, Math.min(x, getMinWidth() - 1)),
- Math.max(0, Math.min(y, getHeight() - 1)));
+ return super.getNearestKeys(Math.max(0, Math.min(x, mOccupiedWidth - 1)),
+ Math.max(0, Math.min(y, mOccupiedHeight - 1)));
}
public static int getTextSizeFromTheme(Theme theme, int style, int defValue) {
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
index 078d89f49..12aadcb5c 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
@@ -269,7 +269,7 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO: Should notify InputMethodService instead?
- KeyboardSwitcher.getInstance().onSizeChanged();
+ KeyboardSwitcher.getInstance().onSizeChanged(w, h, oldw, oldh);
}
/**
@@ -281,15 +281,12 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
*/
@Override
public void setKeyboard(Keyboard keyboard) {
- if (getKeyboard() != null) {
- PointerTracker.dismissAllKeyPreviews();
- }
// Remove any pending messages, except dismissing preview
mKeyTimerHandler.cancelKeyTimers();
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(
keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
- mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
+ mKeyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
PointerTracker.setKeyDetector(mKeyDetector);
mPopupPanelCache.clear();
}
@@ -348,7 +345,6 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
}
// This default implementation returns a popup mini keyboard panel.
- // A derived class may return a language switcher popup panel, for instance.
protected PopupPanel onCreatePopupPanel(Key parentKey) {
if (parentKey.mPopupCharacters == null)
return null;
@@ -361,7 +357,7 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
(PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view);
final Keyboard parentKeyboard = getKeyboard();
final Keyboard miniKeyboard = new MiniKeyboardBuilder(
- this, parentKeyboard.getPopupKeyboardResId(), parentKey, parentKeyboard).build();
+ this, parentKeyboard.mPopupKeyboardResId, parentKey, parentKeyboard).build();
miniKeyboardView.setKeyboard(miniKeyboard);
container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
@@ -401,11 +397,10 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
mPopupPanel = popupPanel;
mPopupPanelPointerTrackerId = tracker.mPointerId;
- tracker.onLongPressed();
- popupPanel.showPanel(this, parentKey, tracker, mPopupWindow);
+ popupPanel.showPopupPanel(this, parentKey, tracker, mPopupWindow);
final int translatedX = popupPanel.translateX(tracker.getLastX());
final int translatedY = popupPanel.translateY(tracker.getLastY());
- tracker.onDownEvent(translatedX, translatedY, SystemClock.uptimeMillis(), popupPanel);
+ tracker.onShowPopupPanel(translatedX, translatedY, SystemClock.uptimeMillis(), popupPanel);
invalidateAllKeys();
return true;
@@ -547,11 +542,12 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
@Override
public void closing() {
super.closing();
- dismissMiniKeyboard();
+ dismissPopupPanel();
mPopupPanelCache.clear();
}
- public boolean dismissMiniKeyboard() {
+ @Override
+ public boolean dismissPopupPanel() {
if (mPopupWindow != null && mPopupWindow.isShowing()) {
mPopupWindow.dismiss();
mPopupPanel = null;
@@ -563,14 +559,14 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
}
public boolean handleBack() {
- return dismissMiniKeyboard();
+ return dismissPopupPanel();
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
+ // Drop non-hover touch events when touch exploration is enabled.
if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event)
- || super.dispatchTouchEvent(event);
+ return false;
}
return super.dispatchTouchEvent(event);
@@ -587,15 +583,22 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
return super.dispatchPopulateAccessibilityEvent(event);
}
- public boolean onHoverEvent(MotionEvent event) {
- // Since reflection doesn't support calling superclass methods, this
- // method checks for the existence of onHoverEvent() in the View class
- // before returning a value.
+ /**
+ * Receives hover events from the input framework. This method overrides
+ * View.dispatchHoverEvent(MotionEvent) on SDK version ICS or higher. On
+ * lower SDK versions, this method is never called.
+ *
+ * @param event The motion event to be dispatched.
+ * @return {@code true} if the event was handled by the view, {@code false}
+ * otherwise
+ */
+ public boolean dispatchHoverEvent(MotionEvent event) {
if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
final PointerTracker tracker = getPointerTracker(0);
- return AccessibleKeyboardViewProxy.getInstance().onHoverEvent(event, tracker);
+ return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
}
+ // Reflection doesn't support calling superclass methods.
return false;
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 617961b59..dad37e728 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -23,6 +23,7 @@ import android.util.Log;
import android.view.MotionEvent;
import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.Utils;
@@ -40,8 +41,6 @@ public class LatinKeyboardView extends LatinKeyboardBaseView {
private boolean mDisableDisambiguation;
/** The distance threshold at which we start treating the touch session as a multi-touch */
private int mJumpThresholdSquare = Integer.MAX_VALUE;
- /** The y coordinate of the last row */
- private int mLastRowY;
private int mLastX;
private int mLastY;
@@ -55,57 +54,60 @@ public class LatinKeyboardView extends LatinKeyboardBaseView {
@Override
public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
- LatinKeyboard latinKeyboard = getLatinKeyboard();
- if (latinKeyboard != null
- && (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard())) {
- // Phone and number keyboard never shows popup preview (except language switch).
- super.setKeyPreviewPopupEnabled(false, delay);
- } else {
- super.setKeyPreviewPopupEnabled(previewEnabled, delay);
+ final Keyboard keyboard = getKeyboard();
+ if (keyboard instanceof LatinKeyboard) {
+ final LatinKeyboard latinKeyboard = (LatinKeyboard)keyboard;
+ if (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard()) {
+ // Phone and number keyboard never shows popup preview.
+ super.setKeyPreviewPopupEnabled(false, delay);
+ return;
+ }
}
+ super.setKeyPreviewPopupEnabled(previewEnabled, delay);
}
@Override
public void setKeyboard(Keyboard newKeyboard) {
super.setKeyboard(newKeyboard);
// One-seventh of the keyboard width seems like a reasonable threshold
- mJumpThresholdSquare = newKeyboard.getMinWidth() / 7;
- mJumpThresholdSquare *= mJumpThresholdSquare;
- // Assuming there are 4 rows, this is the coordinate of the last row
- mLastRowY = (newKeyboard.getHeight() * 3) / 4;
- }
-
- private LatinKeyboard getLatinKeyboard() {
- Keyboard keyboard = getKeyboard();
- if (keyboard instanceof LatinKeyboard) {
- return (LatinKeyboard)keyboard;
- } else {
- return null;
- }
+ final int jumpThreshold = newKeyboard.mOccupiedWidth / 7;
+ mJumpThresholdSquare = jumpThreshold * jumpThreshold;
}
public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboard oldKeyboard) {
- final LatinKeyboard currentKeyboard = getLatinKeyboard();
+ final Keyboard keyboard = getKeyboard();
// We should not set text fade factor to the keyboard which does not display the language on
// its spacebar.
- if (currentKeyboard != null && currentKeyboard == oldKeyboard)
- currentKeyboard.setSpacebarTextFadeFactor(fadeFactor, this);
+ if (keyboard instanceof LatinKeyboard && keyboard == oldKeyboard) {
+ ((LatinKeyboard)keyboard).setSpacebarTextFadeFactor(fadeFactor, this);
+ }
}
@Override
protected boolean onLongPress(Key key, PointerTracker tracker) {
- int primaryCode = key.mCode;
+ final int primaryCode = key.mCode;
+ final Keyboard keyboard = getKeyboard();
+ if (keyboard instanceof LatinKeyboard) {
+ final LatinKeyboard latinKeyboard = (LatinKeyboard) keyboard;
+ if (primaryCode == Keyboard.CODE_DIGIT0 && latinKeyboard.isPhoneKeyboard()) {
+ tracker.onLongPressed();
+ // Long pressing on 0 in phone number keypad gives you a '+'.
+ return invokeOnKey(Keyboard.CODE_PLUS);
+ }
+ if (primaryCode == Keyboard.CODE_SHIFT && latinKeyboard.isAlphaKeyboard()) {
+ tracker.onLongPressed();
+ return invokeOnKey(Keyboard.CODE_CAPSLOCK);
+ }
+ }
if (primaryCode == Keyboard.CODE_SETTINGS || primaryCode == Keyboard.CODE_SPACE) {
- tracker.onLongPressed();
// Both long pressing settings key and space key invoke IME switcher dialog.
- return invokeOnKey(Keyboard.CODE_SETTINGS_LONGPRESS);
- } else if (primaryCode == '0' && getLatinKeyboard().isPhoneKeyboard()) {
- tracker.onLongPressed();
- // Long pressing on 0 in phone number keypad gives you a '+'.
- return invokeOnKey('+');
- } else if (primaryCode == Keyboard.CODE_SHIFT) {
- tracker.onLongPressed();
- return invokeOnKey(Keyboard.CODE_CAPSLOCK);
+ if (getKeyboardActionListener().onCustomRequest(
+ LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
+ tracker.onLongPressed();
+ return true;
+ } else {
+ return super.onLongPress(key, tracker);
+ }
} else {
return super.onLongPress(key, tracker);
}
@@ -127,7 +129,7 @@ public class LatinKeyboardView extends LatinKeyboardBaseView {
* the sudden moves subside, a DOWN event is simulated for the second key.
* @param me the motion event
* @return true if the event was consumed, so that it doesn't continue to be handled by
- * KeyboardView.
+ * {@link LatinKeyboardBaseView}.
*/
private boolean handleSuddenJump(MotionEvent me) {
// If device has distinct multi touch panel, there is no need to check sudden jump.
@@ -157,11 +159,8 @@ public class LatinKeyboardView extends LatinKeyboardBaseView {
case MotionEvent.ACTION_MOVE:
// Is this a big jump?
final int distanceSquare = (mLastX - x) * (mLastX - x) + (mLastY - y) * (mLastY - y);
- // Check the distance and also if the move is not entirely within the bottom row
- // If it's only in the bottom row, it might be an intentional slide gesture
- // for language switching
- if (distanceSquare > mJumpThresholdSquare
- && (mLastY < mLastRowY || y < mLastRowY)) {
+ // Check the distance.
+ if (distanceSquare > mJumpThresholdSquare) {
// If we're not yet dropping events, start dropping and send an UP event
if (!mDroppingEvents) {
mDroppingEvents = true;
@@ -201,7 +200,7 @@ public class LatinKeyboardView extends LatinKeyboardBaseView {
@Override
public boolean onTouchEvent(MotionEvent me) {
- if (getLatinKeyboard() == null) return true;
+ if (getKeyboard() == null) return true;
// If there was a sudden jump, return without processing the actual motion event.
if (handleSuddenJump(me)) {
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
index 44f9f195c..08e7d7e19 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
@@ -16,28 +16,14 @@
package com.android.inputmethod.keyboard;
-import android.content.Context;
+import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardParams;
public class MiniKeyboard extends Keyboard {
- private int mDefaultKeyCoordX;
+ private final int mDefaultKeyCoordX;
- public MiniKeyboard(Context context, int xmlLayoutResId, Keyboard parentKeyboard) {
- super(context, xmlLayoutResId, parentKeyboard.mId.cloneAsMiniKeyboard(),
- parentKeyboard.getMinWidth());
- // HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal
- // and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are
- // needed to keep having the same horizontal and vertical key spacing.
- setHorizontalGap(0);
- setVerticalGap(parentKeyboard.getVerticalGap() / 2);
-
- // TODO: When we have correctly padded key background 9-patch drawables for mini keyboard,
- // revert the above hacks and uncomment the following lines.
- //setHorizontalGap(parentKeyboard.getHorizontalGap());
- //setVerticalGap(parentKeyboard.getVerticalGap());
- }
-
- public void setDefaultCoordX(int pos) {
- mDefaultKeyCoordX = pos;
+ public MiniKeyboard(MiniKeyboardParams params) {
+ super(params);
+ mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2;
}
public int getDefaultCoordX() {
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
index 1ec0dda15..84bd44c30 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
@@ -37,7 +37,7 @@ public class MiniKeyboardKeyDetector extends KeyDetector {
@Override
public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
- final List<Key> keys = getKeyboard().getKeys();
+ final List<Key> keys = getKeyboard().mKeys;
final int touchX = getTouchX(x);
final int touchY = getTouchY(y);
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 8d7496c54..1f8119a0f 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -19,11 +19,11 @@ package com.android.inputmethod.keyboard;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
+import android.widget.TextView;
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
import java.util.ArrayList;
import java.util.Arrays;
@@ -65,9 +65,11 @@ public class PointerTracker {
public interface DrawingProxy {
public void invalidateKey(Key key);
+ public TextView inflateKeyPreviewText();
public void showKeyPreview(int keyIndex, PointerTracker tracker);
public void cancelShowKeyPreview(PointerTracker tracker);
public void dismissKeyPreview(PointerTracker tracker);
+ public boolean dismissPopupPanel();
}
public interface TimerProxy {
@@ -100,6 +102,7 @@ public class PointerTracker {
private Keyboard mKeyboard;
private List<Key> mKeys;
private int mKeyQuarterWidthSquared;
+ private final TextView mKeyPreviewText;
// The position and time at which first down event occurred.
private long mDownTime;
@@ -118,9 +121,12 @@ public class PointerTracker {
// true if keyboard layout has been changed.
private boolean mKeyboardLayoutHasBeenChanged;
- // true if event is already translated to a key action (long press or mini-keyboard)
+ // true if event is already translated to a key action.
private boolean mKeyAlreadyProcessed;
+ // true if this pointer has been long-pressed and is showing a popup panel.
+ private boolean mIsShowingPopupPanel;
+
// true if this pointer is repeatable key
private boolean mIsRepeatableKey;
@@ -133,12 +139,6 @@ public class PointerTracker {
// ignore modifier key if true
private boolean mIgnoreModifierKey;
- // TODO: Remove these hacking variables
- // true if this pointer is in sliding language switch
- private boolean mIsInSlidingLanguageSwitch;
- private int mSpaceKeyIndex;
- private static SubtypeSwitcher sSubtypeSwitcher;
-
// Empty {@link KeyboardActionListener}
private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() {
@Override
@@ -151,6 +151,8 @@ public class PointerTracker {
public void onTextInput(CharSequence text) {}
@Override
public void onCancelInput() {}
+ @Override
+ public boolean onCustomRequest(int requestCode) { return false; }
};
public static void init(boolean hasDistinctMultitouch, Context context) {
@@ -172,7 +174,6 @@ public class PointerTracker {
sTouchNoiseThresholdDistanceSquared = (int)(
touchNoiseThresholdDistance * touchNoiseThresholdDistance);
sKeyboardSwitcher = KeyboardSwitcher.getInstance();
- sSubtypeSwitcher = SubtypeSwitcher.getInstance();
}
public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
@@ -207,8 +208,7 @@ public class PointerTracker {
public static void dismissAllKeyPreviews() {
for (final PointerTracker tracker : sTrackers) {
- tracker.setReleasedKeyGraphics();
- tracker.dismissKeyPreview();
+ tracker.setReleasedKeyGraphics(tracker.mKeyIndex);
}
}
@@ -220,6 +220,11 @@ public class PointerTracker {
mListener = handler.getKeyboardActionListener();
mDrawingProxy = handler.getDrawingProxy();
mTimerProxy = handler.getTimerProxy();
+ mKeyPreviewText = mDrawingProxy.inflateKeyPreviewText();
+ }
+
+ public TextView getKeyPreviewText() {
+ return mKeyPreviewText;
}
// Returns true if keyboard has been changed by this callback.
@@ -282,8 +287,8 @@ public class PointerTracker {
public void setKeyDetectorInner(KeyDetector keyDetector) {
mKeyDetector = keyDetector;
mKeyboard = keyDetector.getKeyboard();
- mKeys = mKeyboard.getKeys();
- final int keyQuarterWidth = mKeyboard.getKeyWidth() / 4;
+ mKeys = mKeyboard.mKeys;
+ final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
}
@@ -326,18 +331,10 @@ public class PointerTracker {
return mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null);
}
- public boolean isSpaceKey(int keyIndex) {
- Key key = getKey(keyIndex);
- return key != null && key.mCode == Keyboard.CODE_SPACE;
- }
-
- public void setReleasedKeyGraphics() {
- setReleasedKeyGraphics(mKeyIndex);
- }
-
private void setReleasedKeyGraphics(int keyIndex) {
+ mDrawingProxy.dismissKeyPreview(this);
final Key key = getKey(keyIndex);
- if (key != null) {
+ if (key != null && key.isEnabled()) {
key.onReleased();
mDrawingProxy.invalidateKey(key);
}
@@ -346,11 +343,23 @@ public class PointerTracker {
private void setPressedKeyGraphics(int keyIndex) {
final Key key = getKey(keyIndex);
if (key != null && key.isEnabled()) {
+ if (isKeyPreviewRequired(key)) {
+ mDrawingProxy.showKeyPreview(keyIndex, this);
+ }
key.onPressed();
mDrawingProxy.invalidateKey(key);
}
}
+ // The modifier key, such as shift key, should not show its key preview.
+ private static boolean isKeyPreviewRequired(Key key) {
+ final int code = key.mCode;
+ if (isModifierCode(code) || code == Keyboard.CODE_DELETE
+ || code == Keyboard.CODE_ENTER || code == Keyboard.CODE_SPACE)
+ return false;
+ return true;
+ }
+
public int getLastX() {
return mLastX;
}
@@ -436,7 +445,6 @@ public class PointerTracker {
mKeyAlreadyProcessed = false;
mIsRepeatableKey = false;
mIsInSlidingKeyInput = false;
- mIsInSlidingLanguageSwitch = false;
mIgnoreModifierKey = false;
if (isValidKeyIndex(keyIndex)) {
// This onPress call may have changed keyboard layout. Those cases are detected at
@@ -447,7 +455,6 @@ public class PointerTracker {
startRepeatKey(keyIndex);
startLongPressTimer(keyIndex);
- showKeyPreview(keyIndex);
setPressedKeyGraphics(keyIndex);
}
}
@@ -464,12 +471,6 @@ public class PointerTracker {
if (mKeyAlreadyProcessed)
return;
- // TODO: Remove this hacking code
- if (mIsInSlidingLanguageSwitch) {
- ((LatinKeyboard)mKeyboard).updateSpacebarPreviewIcon(x - mKeyX);
- showKeyPreview(mSpaceKeyIndex);
- return;
- }
final int lastX = mLastX;
final int lastY = mLastY;
final int oldKeyIndex = mKeyIndex;
@@ -486,7 +487,6 @@ public class PointerTracker {
keyIndex = onMoveKey(x, y);
onMoveToNewKey(keyIndex, x, y);
startLongPressTimer(keyIndex);
- showKeyPreview(keyIndex);
setPressedKeyGraphics(keyIndex);
} else if (isMajorEnoughMoveToBeOnNewKey(x, y, keyIndex)) {
// The pointer has been slid in to the new key from the previous key, we must call
@@ -506,7 +506,6 @@ public class PointerTracker {
onMoveToNewKey(keyIndex, x, y);
startLongPressTimer(keyIndex);
setPressedKeyGraphics(keyIndex);
- showKeyPreview(keyIndex);
} else {
// HACK: On some devices, quick successive touches may be translated to sudden
// move by touch panel firmware. This hack detects the case and translates the
@@ -518,35 +517,14 @@ public class PointerTracker {
if (DEBUG_MODE)
Log.w(TAG, String.format("onMoveEvent: sudden move is translated to "
+ "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
- onUpEventInternal(lastX, lastY, eventTime, true);
+ onUpEventInternal(lastX, lastY, eventTime);
onDownEventInternal(x, y, eventTime);
} else {
mKeyAlreadyProcessed = true;
- dismissKeyPreview();
setReleasedKeyGraphics(oldKeyIndex);
}
}
}
- // TODO: Remove this hack code
- else if (isSpaceKey(keyIndex) && !mIsInSlidingLanguageSwitch
- && mKeyboard instanceof LatinKeyboard) {
- final LatinKeyboard keyboard = ((LatinKeyboard)mKeyboard);
- if (sSubtypeSwitcher.useSpacebarLanguageSwitcher()
- && sSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) {
- final int diff = x - mKeyX;
- if (keyboard.shouldTriggerSpacebarSlidingLanguageSwitch(diff)) {
- // Detect start sliding language switch.
- mIsInSlidingLanguageSwitch = true;
- mSpaceKeyIndex = keyIndex;
- keyboard.updateSpacebarPreviewIcon(diff);
- // Display spacebar slide language switcher.
- showKeyPreview(keyIndex);
- final PointerTrackerQueue queue = sPointerTrackerQueue;
- if (queue != null)
- queue.releaseAllPointersExcept(this, eventTime, true);
- }
- }
- }
} else {
if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, keyIndex)) {
// The pointer has been slid out from the previous key, we must call onRelease() to
@@ -559,7 +537,6 @@ public class PointerTracker {
onMoveToNewKey(keyIndex, x, y);
} else {
mKeyAlreadyProcessed = true;
- dismissKeyPreview();
}
}
}
@@ -574,27 +551,26 @@ public class PointerTracker {
if (isModifier()) {
// Before processing an up event of modifier key, all pointers already being
// tracked should be released.
- queue.releaseAllPointersExcept(this, eventTime, true);
+ queue.releaseAllPointersExcept(this, eventTime);
} else {
queue.releaseAllPointersOlderThan(this, eventTime);
}
queue.remove(this);
}
- onUpEventInternal(x, y, eventTime, true);
+ onUpEventInternal(x, y, eventTime);
}
// Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
// This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
// "virtual" up event.
- public void onPhantomUpEvent(int x, int y, long eventTime, boolean updateReleasedKeyGraphics) {
+ public void onPhantomUpEvent(int x, int y, long eventTime) {
if (DEBUG_EVENT)
printTouchEvent("onPhntEvent:", x, y, eventTime);
- onUpEventInternal(x, y, eventTime, updateReleasedKeyGraphics);
+ onUpEventInternal(x, y, eventTime);
mKeyAlreadyProcessed = true;
}
- private void onUpEventInternal(int x, int y, long eventTime,
- boolean updateReleasedKeyGraphics) {
+ private void onUpEventInternal(int x, int y, long eventTime) {
mTimerProxy.cancelKeyTimers();
mDrawingProxy.cancelShowKeyPreview(this);
mIsInSlidingKeyInput = false;
@@ -608,34 +584,27 @@ public class PointerTracker {
keyY = mKeyY;
}
final int keyIndex = onUpKey(keyX, keyY, eventTime);
- dismissKeyPreview();
- if (updateReleasedKeyGraphics)
- setReleasedKeyGraphics(keyIndex);
+ setReleasedKeyGraphics(keyIndex);
+ if (mIsShowingPopupPanel) {
+ mDrawingProxy.dismissPopupPanel();
+ mIsShowingPopupPanel = false;
+ }
if (mKeyAlreadyProcessed)
return;
- // TODO: Remove this hacking code
- if (mIsInSlidingLanguageSwitch) {
- setReleasedKeyGraphics(mSpaceKeyIndex);
- final int languageDir = ((LatinKeyboard)mKeyboard).getLanguageChangeDirection();
- if (languageDir != 0) {
- final int code = (languageDir == 1)
- ? LatinKeyboard.CODE_NEXT_LANGUAGE : LatinKeyboard.CODE_PREV_LANGUAGE;
- // This will change keyboard layout.
- mListener.onCodeInput(code, new int[] {code}, keyX, keyY);
- }
- mIsInSlidingLanguageSwitch = false;
- ((LatinKeyboard)mKeyboard).setSpacebarSlidingLanguageSwitchDiff(0);
- return;
- }
if (!mIsRepeatableKey) {
detectAndSendKey(keyIndex, keyX, keyY);
}
}
+ public void onShowPopupPanel(int x, int y, long eventTime, KeyEventHandler handler) {
+ onLongPressed();
+ onDownEvent(x, y, eventTime, handler);
+ mIsShowingPopupPanel = true;
+ }
+
public void onLongPressed() {
mKeyAlreadyProcessed = true;
- setReleasedKeyGraphics();
- dismissKeyPreview();
+ setReleasedKeyGraphics(mKeyIndex);
final PointerTrackerQueue queue = sPointerTrackerQueue;
if (queue != null) {
queue.remove(this);
@@ -648,7 +617,7 @@ public class PointerTracker {
final PointerTrackerQueue queue = sPointerTrackerQueue;
if (queue != null) {
- queue.releaseAllPointersExcept(this, eventTime, true);
+ queue.releaseAllPointersExcept(this, eventTime);
queue.remove(this);
}
onCancelEventInternal();
@@ -657,15 +626,17 @@ public class PointerTracker {
private void onCancelEventInternal() {
mTimerProxy.cancelKeyTimers();
mDrawingProxy.cancelShowKeyPreview(this);
- dismissKeyPreview();
setReleasedKeyGraphics(mKeyIndex);
mIsInSlidingKeyInput = false;
+ if (mIsShowingPopupPanel) {
+ mDrawingProxy.dismissPopupPanel();
+ mIsShowingPopupPanel = false;
+ }
}
private void startRepeatKey(int keyIndex) {
final Key key = getKey(keyIndex);
if (key != null && key.mRepeatable) {
- dismissKeyPreview();
onRepeatKey(keyIndex);
mTimerProxy.startKeyRepeatTimer(sDelayBeforeKeyRepeatStart, keyIndex, this);
mIsRepeatableKey = true;
@@ -695,31 +666,9 @@ public class PointerTracker {
}
}
- // The modifier key, such as shift key, should not show its key preview.
- private boolean isKeyPreviewNotRequired(int keyIndex) {
- final Key key = getKey(keyIndex);
- if (key == null || !key.isEnabled())
- return true;
- // Such as spacebar sliding language switch.
- if (mKeyboard.needSpacebarPreview(keyIndex))
- return false;
- final int code = key.mCode;
- return isModifierCode(code) || code == Keyboard.CODE_DELETE
- || code == Keyboard.CODE_ENTER || code == Keyboard.CODE_SPACE;
- }
-
- private void showKeyPreview(int keyIndex) {
- if (isKeyPreviewNotRequired(keyIndex))
- return;
- mDrawingProxy.showKeyPreview(keyIndex, this);
- }
-
- private void dismissKeyPreview() {
- mDrawingProxy.dismissKeyPreview(this);
- }
-
private void startLongPressTimer(int keyIndex) {
Key key = getKey(keyIndex);
+ if (key == null) return;
if (key.mCode == Keyboard.CODE_SHIFT) {
if (sLongPressShiftKeyTimeout > 0) {
mTimerProxy.startLongPressTimer(sLongPressShiftKeyTimeout, keyIndex, this);
diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
index 5ab44d063..fb932e3e8 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
@@ -59,19 +59,16 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
mParentKeyboardView.getKeyboardActionListener()
.onCodeInput(primaryCode, keyCodes, x, y);
- mParentKeyboardView.dismissMiniKeyboard();
}
@Override
public void onTextInput(CharSequence text) {
mParentKeyboardView.getKeyboardActionListener().onTextInput(text);
- mParentKeyboardView.dismissMiniKeyboard();
}
@Override
public void onCancelInput() {
mParentKeyboardView.getKeyboardActionListener().onCancelInput();
- mParentKeyboardView.dismissMiniKeyboard();
}
@Override
@@ -82,6 +79,8 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
public void onRelease(int primaryCode, boolean withSliding) {
mParentKeyboardView.getKeyboardActionListener().onRelease(primaryCode, withSliding);
}
+ @Override
+ public boolean onCustomRequest(int requestCode) { return false; }
};
public PopupMiniKeyboardView(Context context, AttributeSet attrs) {
@@ -108,6 +107,18 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final Keyboard keyboard = getKeyboard();
+ if (keyboard != null) {
+ final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
+ final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
+ setMeasuredDimension(width, height);
+ } else {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
public void setKeyboard(Keyboard keyboard) {
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
@@ -147,7 +158,7 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
}
@Override
- public void showPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
+ public void showPopupPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
PointerTracker tracker, PopupWindow window) {
mParentKeyboardView = parentKeyboardView;
final View container = (View)getParent();
@@ -161,9 +172,9 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
final int miniKeyboardLeft = pointX - miniKeyboard.getDefaultCoordX()
+ parentKeyboardView.getPaddingLeft();
final int x = Math.max(0, Math.min(miniKeyboardLeft,
- parentKeyboardView.getWidth() - miniKeyboard.getMinWidth()))
+ parentKeyboardView.getWidth() - miniKeyboard.mOccupiedWidth))
- container.getPaddingLeft() + mCoordinates[0];
- final int y = pointY - parentKeyboard.getVerticalGap()
+ final int y = pointY - parentKeyboard.mVerticalGap
- (container.getMeasuredHeight() - container.getPaddingBottom())
+ parentKeyboardView.getPaddingTop() + mCoordinates[1];
@@ -180,6 +191,11 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
}
@Override
+ public boolean dismissPopupPanel() {
+ return mParentKeyboardView.dismissPopupPanel();
+ }
+
+ @Override
public int translateX(int x) {
return x - mOriginX;
}
diff --git a/java/src/com/android/inputmethod/keyboard/PopupPanel.java b/java/src/com/android/inputmethod/keyboard/PopupPanel.java
index f94d1c562..dc526e74f 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupPanel.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupPanel.java
@@ -26,7 +26,7 @@ public interface PopupPanel extends PointerTracker.KeyEventHandler {
* @param tracker the pointer tracker that pressesd the parent key
* @param window PopupWindow to be used to show this popup panel
*/
- public void showPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
+ public void showPopupPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
PointerTracker tracker, PopupWindow window);
/**
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index aadedc69d..5e73d6300 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -17,8 +17,10 @@
package com.android.inputmethod.keyboard;
import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
public class ProximityInfo {
@@ -54,6 +56,19 @@ public class ProximityInfo {
computeNearestNeighbors(keyWidth, keys);
}
+ public static ProximityInfo getDummyProximityInfo() {
+ return new ProximityInfo(1, 1, 1, 1, 1, Collections.<Key>emptyList());
+ }
+
+ public static ProximityInfo getSpellCheckerProximityInfo() {
+ final ProximityInfo spellCheckerProximityInfo = getDummyProximityInfo();
+ spellCheckerProximityInfo.mNativeProximityInfo =
+ spellCheckerProximityInfo.setProximityInfoNative(
+ SpellCheckerProximityInfo.ROW_SIZE,
+ 480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY);
+ return spellCheckerProximityInfo;
+ }
+
private int mNativeProximityInfo;
static {
Utils.loadNativeLibrary();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
index 30d9692a8..c0dba4173 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -20,7 +20,7 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.Log;
-import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException;
import com.android.inputmethod.latin.R;
import java.util.ArrayList;
@@ -212,19 +212,19 @@ public class KeyStyles {
public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs,
XmlResourceParser parser) {
- String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
+ final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
if (DEBUG) Log.d(TAG, String.format("<%s styleName=%s />",
- KeyboardParser.TAG_KEY_STYLE, styleName));
+ KeyboardBuilder.TAG_KEY_STYLE, styleName));
if (mStyles.containsKey(styleName))
throw new ParseException("duplicate key style declared: " + styleName, parser);
final DeclaredKeyStyle style = new DeclaredKeyStyle();
if (keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_parentStyle)) {
- String parentStyle = keyStyleAttr.getString(
+ final String parentStyle = keyStyleAttr.getString(
R.styleable.Keyboard_KeyStyle_parentStyle);
final DeclaredKeyStyle parent = mStyles.get(parentStyle);
if (parent == null)
- throw new ParseException("Unknown parentStyle " + parent, parser);
+ throw new ParseException("Unknown parentStyle " + parentStyle, parser);
style.addParent(parent);
}
style.parseKeyStyleAttributes(keyAttrs);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index e35db8955..de04ecd6c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.util.Xml;
@@ -36,12 +37,11 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.Arrays;
-import java.util.List;
/**
- * Parser for BaseKeyboard.
+ * Keyboard Building helper.
*
- * This class parses Keyboard XML file and fill out keys in Keyboard.
+ * This class parses Keyboard XML file and eventually build a Keyboard.
* The Keyboard XML file looks like:
* <pre>
* &gt;!-- xml/keyboard.xml --&lt;
@@ -107,8 +107,8 @@ import java.util.List;
* </pre>
*/
-public class KeyboardParser {
- private static final String TAG = KeyboardParser.class.getSimpleName();
+public class KeyboardBuilder<KP extends KeyboardParams> {
+ private static final String TAG = KeyboardBuilder.class.getSimpleName();
private static final boolean DEBUG = false;
// Keyboard XML Tags
@@ -123,38 +123,52 @@ public class KeyboardParser {
private static final String TAG_DEFAULT = "default";
public static final String TAG_KEY_STYLE = "key-style";
- private final Keyboard mKeyboard;
- private final Context mContext;
- private final Resources mResources;
+ protected final KP mParams;
+ protected final Context mContext;
+ protected final Resources mResources;
+ private final DisplayMetrics mDisplayMetrics;
- private int mKeyboardTopPadding;
- private int mKeyboardBottomPadding;
- private int mHorizontalEdgesPadding;
private int mCurrentX = 0;
private int mCurrentY = 0;
- private int mMaxRowWidth = 0;
- private int mTotalHeight = 0;
private Row mCurrentRow = null;
+ private boolean mLeftEdge;
+ private Key mRightEdgeKey = null;
private final KeyStyles mKeyStyles = new KeyStyles();
- public KeyboardParser(Keyboard keyboard, Context context) {
- mKeyboard = keyboard;
+ public KeyboardBuilder(Context context, KP params) {
mContext = context;
final Resources res = context.getResources();
mResources = res;
- mHorizontalEdgesPadding = (int)res.getDimension(R.dimen.keyboard_horizontal_edges_padding);
+ mDisplayMetrics = res.getDisplayMetrics();
+
+ mParams = params;
+ mParams.mHorizontalEdgesPadding = (int)res.getDimension(
+ R.dimen.keyboard_horizontal_edges_padding);
+
+ mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
+ mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
}
- public int getMaxRowWidth() {
- return mMaxRowWidth;
+ public KeyboardBuilder<KP> load(KeyboardId id) {
+ mParams.mId = id;
+ try {
+ parseKeyboard(id.getXmlId());
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "keyboard XML parse error: " + e);
+ throw new IllegalArgumentException(e);
+ } catch (IOException e) {
+ Log.w(TAG, "keyboard XML parse error: " + e);
+ throw new RuntimeException(e);
+ }
+ return this;
}
- public int getTotalHeight() {
- return mTotalHeight;
+ public Keyboard build() {
+ return new Keyboard(mParams);
}
- public void parseKeyboard(int resId) throws XmlPullParserException, IOException {
- if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mKeyboard.mId));
+ private void parseKeyboard(int resId) throws XmlPullParserException, IOException {
+ if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId));
final XmlResourceParser parser = mResources.getXml(resId);
int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -163,7 +177,7 @@ public class KeyboardParser {
if (TAG_KEYBOARD.equals(tag)) {
parseKeyboardAttributes(parser);
startKeyboard();
- parseKeyboardContent(parser, mKeyboard.getKeys());
+ parseKeyboardContent(parser, false);
break;
} else {
throw new IllegalStartTag(parser, TAG_KEYBOARD);
@@ -194,15 +208,14 @@ public class KeyboardParser {
}
private void parseKeyboardAttributes(XmlResourceParser parser) {
- final Keyboard keyboard = mKeyboard;
- final int displayWidth = keyboard.getDisplayWidth();
+ final int displayWidth = mDisplayMetrics.widthPixels;
final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
R.style.Keyboard);
final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard_Key);
try {
- final int displayHeight = keyboard.getDisplayHeight();
+ final int displayHeight = mDisplayMetrics.heightPixels;
final int keyboardHeight = (int)keyboardAttr.getDimension(
R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
final int maxKeyboardHeight = getDimensionOrFraction(keyboardAttr,
@@ -217,60 +230,67 @@ public class KeyboardParser {
}
// Keyboard height will not exceed maxKeyboardHeight and will not be less than
// minKeyboardHeight.
- final int height = Math.max(
+ mParams.mOccupiedHeight = Math.max(
Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
-
-
- keyboard.setKeyboardHeight(height);
- keyboard.setKeyWidth(getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_keyWidth, displayWidth, displayWidth / 10));
- keyboard.setRowHeight(getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_rowHeight, height, 50));
- keyboard.setHorizontalGap(getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_horizontalGap, displayWidth, 0));
- keyboard.setVerticalGap(getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_verticalGap, height, 0));
- keyboard.setPopupKeyboardResId(keyboardAttr.getResourceId(
- R.styleable.Keyboard_popupKeyboardTemplate, 0));
- keyboard.setMaxPopupKeyboardColumn(keyAttr.getInt(
- R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5));
-
- mKeyboard.mIconsSet.loadIcons(keyboardAttr);
- mKeyboardTopPadding = getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_keyboardTopPadding, height, 0);
- mKeyboardBottomPadding = getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_keyboardBottomPadding, height, 0);
+ mParams.mOccupiedWidth = mParams.mId.mWidth;
+ mParams.mTopPadding = getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_keyboardTopPadding, mParams.mOccupiedHeight, 0);
+ mParams.mBottomPadding = getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_keyboardBottomPadding, mParams.mOccupiedHeight, 0);
+
+ final int height = mParams.mOccupiedHeight;
+ final int width = mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding * 2
+ - mParams.mHorizontalCenterPadding;
+ mParams.mHeight = height;
+ mParams.mWidth = width;
+ mParams.mDefaultKeyWidth = getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_keyWidth, width, width / 10);
+ mParams.mDefaultRowHeight = getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_rowHeight, height, height / 4);
+ mParams.mHorizontalGap = getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_horizontalGap, width, 0);
+ mParams.mVerticalGap = getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_verticalGap, height, 0);
+
+ mParams.mIsRtlKeyboard = keyboardAttr.getBoolean(
+ R.styleable.Keyboard_isRtlKeyboard, false);
+ mParams.mPopupKeyboardResId = keyboardAttr.getResourceId(
+ R.styleable.Keyboard_popupKeyboardTemplate, 0);
+ mParams.mMaxPopupColumn = keyAttr.getInt(
+ R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5);
+
+ mParams.mIconsSet.loadIcons(keyboardAttr);
} finally {
keyAttr.recycle();
keyboardAttr.recycle();
}
}
- private void parseKeyboardContent(XmlResourceParser parser, List<Key> keys)
+ private void parseKeyboardContent(XmlResourceParser parser, boolean skip)
throws XmlPullParserException, IOException {
int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if (TAG_ROW.equals(tag)) {
- Row row = new Row(mResources, mKeyboard, parser);
+ Row row = parseRowAttributes(parser);
if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW));
- if (keys != null)
+ if (!skip)
startRow(row);
- parseRowContent(parser, row, keys);
+ parseRowContent(parser, row, skip);
} else if (TAG_INCLUDE.equals(tag)) {
- parseIncludeKeyboardContent(parser, keys);
+ parseIncludeKeyboardContent(parser, skip);
} else if (TAG_SWITCH.equals(tag)) {
- parseSwitchKeyboardContent(parser, keys);
+ parseSwitchKeyboardContent(parser, skip);
} else if (TAG_KEY_STYLE.equals(tag)) {
- parseKeyStyle(parser, keys);
+ parseKeyStyle(parser, skip);
} else {
throw new IllegalStartTag(parser, TAG_ROW);
}
} else if (event == XmlPullParser.END_TAG) {
final String tag = parser.getName();
if (TAG_KEYBOARD.equals(tag)) {
- endKeyboard(mKeyboard.getVerticalGap());
+ endKeyboard();
break;
} else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
|| TAG_MERGE.equals(tag)) {
@@ -285,22 +305,36 @@ public class KeyboardParser {
}
}
- private void parseRowContent(XmlResourceParser parser, Row row, List<Key> keys)
+ private Row parseRowAttributes(XmlResourceParser parser) {
+ final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+ R.styleable.Keyboard);
+ try {
+ if (a.hasValue(R.styleable.Keyboard_horizontalGap))
+ throw new IllegalAttribute(parser, "horizontalGap");
+ if (a.hasValue(R.styleable.Keyboard_verticalGap))
+ throw new IllegalAttribute(parser, "verticalGap");
+ return new Row(mResources, mParams, parser);
+ } finally {
+ a.recycle();
+ }
+ }
+
+ private void parseRowContent(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException {
int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if (TAG_KEY.equals(tag)) {
- parseKey(parser, row, keys);
+ parseKey(parser, row, skip);
} else if (TAG_SPACER.equals(tag)) {
- parseSpacer(parser, row, keys);
+ parseSpacer(parser, row, skip);
} else if (TAG_INCLUDE.equals(tag)) {
- parseIncludeRowContent(parser, row, keys);
+ parseIncludeRowContent(parser, row, skip);
} else if (TAG_SWITCH.equals(tag)) {
- parseSwitchRowContent(parser, row, keys);
+ parseSwitchRowContent(parser, row, skip);
} else if (TAG_KEY_STYLE.equals(tag)) {
- parseKeyStyle(parser, keys);
+ parseKeyStyle(parser, skip);
} else {
throw new IllegalStartTag(parser, TAG_KEY);
}
@@ -308,7 +342,7 @@ public class KeyboardParser {
final String tag = parser.getName();
if (TAG_ROW.equals(tag)) {
if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW));
- if (keys != null)
+ if (!skip)
endRow();
break;
} else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
@@ -324,26 +358,24 @@ public class KeyboardParser {
}
}
- private void parseKey(XmlResourceParser parser, Row row, List<Key> keys)
+ private void parseKey(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException {
- if (keys == null) {
+ if (skip) {
checkEndTag(TAG_KEY, parser);
} else {
- Key key = new Key(mResources, row, mCurrentX, mCurrentY, parser, mKeyStyles);
+ Key key = new Key(mResources, mParams, row, mCurrentX, mCurrentY, parser, mKeyStyles);
if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d popupCharacters=%s />",
TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode,
Arrays.toString(key.mPopupCharacters)));
checkEndTag(TAG_KEY, parser);
- keys.add(key);
- if (key.mCode == Keyboard.CODE_SHIFT)
- mKeyboard.getShiftKeys().add(key);
+ mParams.onAddKey(key);
endKey(key);
}
}
- private void parseSpacer(XmlResourceParser parser, Row row, List<Key> keys)
+ private void parseSpacer(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException {
- if (keys == null) {
+ if (skip) {
checkEndTag(TAG_SPACER, parser);
} else {
if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER));
@@ -351,14 +383,14 @@ public class KeyboardParser {
R.styleable.Keyboard);
if (keyboardAttr.hasValue(R.styleable.Keyboard_horizontalGap))
throw new IllegalAttribute(parser, "horizontalGap");
- final int keyboardWidth = mKeyboard.getDisplayWidth();
+ final int keyboardWidth = mParams.mWidth;
final int keyWidth = getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_keyWidth,
- keyboardWidth, row.mDefaultWidth);
+ keyboardWidth, row.mDefaultKeyWidth);
keyboardAttr.recycle();
final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard_Key);
- int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr,
+ int keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr,
R.styleable.Keyboard_Key_keyXPos, keyboardWidth, mCurrentX);
if (keyXPos < 0) {
// If keyXPos is negative, the actual x-coordinate will be display_width + keyXPos.
@@ -370,19 +402,19 @@ public class KeyboardParser {
}
}
- private void parseIncludeKeyboardContent(XmlResourceParser parser, List<Key> keys)
+ private void parseIncludeKeyboardContent(XmlResourceParser parser, boolean skip)
throws XmlPullParserException, IOException {
- parseIncludeInternal(parser, null, keys);
+ parseIncludeInternal(parser, null, skip);
}
- private void parseIncludeRowContent(XmlResourceParser parser, Row row, List<Key> keys)
+ private void parseIncludeRowContent(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException {
- parseIncludeInternal(parser, row, keys);
+ parseIncludeInternal(parser, row, skip);
}
- private void parseIncludeInternal(XmlResourceParser parser, Row row, List<Key> keys)
+ private void parseIncludeInternal(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException {
- if (keys == null) {
+ if (skip) {
checkEndTag(TAG_INCLUDE, parser);
} else {
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@@ -396,11 +428,11 @@ public class KeyboardParser {
throw new ParseException("No keyboardLayout attribute in <include/>", parser);
if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />",
TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout)));
- parseMerge(mResources.getLayout(keyboardLayout), row, keys);
+ parseMerge(mResources.getLayout(keyboardLayout), row, skip);
}
}
- private void parseMerge(XmlResourceParser parser, Row row, List<Key> keys)
+ private void parseMerge(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException {
int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -408,9 +440,9 @@ public class KeyboardParser {
final String tag = parser.getName();
if (TAG_MERGE.equals(tag)) {
if (row == null) {
- parseKeyboardContent(parser, keys);
+ parseKeyboardContent(parser, skip);
} else {
- parseRowContent(parser, row, keys);
+ parseRowContent(parser, row, skip);
}
break;
} else {
@@ -421,28 +453,28 @@ public class KeyboardParser {
}
}
- private void parseSwitchKeyboardContent(XmlResourceParser parser, List<Key> keys)
+ private void parseSwitchKeyboardContent(XmlResourceParser parser, boolean skip)
throws XmlPullParserException, IOException {
- parseSwitchInternal(parser, null, keys);
+ parseSwitchInternal(parser, null, skip);
}
- private void parseSwitchRowContent(XmlResourceParser parser, Row row, List<Key> keys)
+ private void parseSwitchRowContent(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException {
- parseSwitchInternal(parser, row, keys);
+ parseSwitchInternal(parser, row, skip);
}
- private void parseSwitchInternal(XmlResourceParser parser, Row row, List<Key> keys)
+ private void parseSwitchInternal(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException {
- if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mKeyboard.mId));
+ if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId));
boolean selected = false;
int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if (TAG_CASE.equals(tag)) {
- selected |= parseCase(parser, row, selected ? null : keys);
+ selected |= parseCase(parser, row, selected ? true : skip);
} else if (TAG_DEFAULT.equals(tag)) {
- selected |= parseDefault(parser, row, selected ? null : keys);
+ selected |= parseDefault(parser, row, selected ? true : skip);
} else {
throw new IllegalStartTag(parser, TAG_KEY);
}
@@ -458,21 +490,21 @@ public class KeyboardParser {
}
}
- private boolean parseCase(XmlResourceParser parser, Row row, List<Key> keys)
+ private boolean parseCase(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException {
final boolean selected = parseCaseCondition(parser);
if (row == null) {
// Processing Rows.
- parseKeyboardContent(parser, selected ? keys : null);
+ parseKeyboardContent(parser, selected ? skip : true);
} else {
// Processing Keys.
- parseRowContent(parser, row, selected ? keys : null);
+ parseRowContent(parser, row, selected ? skip : true);
}
return selected;
}
private boolean parseCaseCondition(XmlResourceParser parser) {
- final KeyboardId id = mKeyboard.mId;
+ final KeyboardId id = mParams.mId;
if (id == null)
return true;
@@ -493,10 +525,10 @@ public class KeyboardParser {
R.styleable.Keyboard_Case_f2KeyMode, id.mF2KeyMode);
final boolean clobberSettingsKeyMatched = matchBoolean(a,
R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
- final boolean voiceEnabledMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_voiceKeyEnabled, id.mVoiceKeyEnabled);
- final boolean voiceKeyMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_hasVoiceKey, id.mHasVoiceKey);
+ final boolean shortcutKeyEnabledMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
+ final boolean hasShortcutKeyMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
// As noted at {@link KeyboardId} class, we are interested only in enum value masked by
// {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and
// {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching
@@ -511,7 +543,7 @@ public class KeyboardParser {
R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
final boolean selected = modeMatched && navigateActionMatched && passwordInputMatched
&& hasSettingsKeyMatched && f2KeyModeMatched && clobberSettingsKeyMatched
- && voiceEnabledMatched && voiceKeyMatched && imeActionMatched &&
+ && shortcutKeyEnabledMatched && hasShortcutKeyMatched && imeActionMatched &&
localeCodeMatched && languageCodeMatched && countryCodeMatched;
if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE,
@@ -523,8 +555,9 @@ public class KeyboardParser {
a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"),
booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
"clobberSettingsKey"),
- booleanAttr(a, R.styleable.Keyboard_Case_voiceKeyEnabled, "voiceKeyEnabled"),
- booleanAttr(a, R.styleable.Keyboard_Case_hasVoiceKey, "hasVoiceKey"),
+ booleanAttr(
+ a, R.styleable.Keyboard_Case_shortcutKeyEnabled, "shortcutKeyEnabled"),
+ booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"),
textAttr(EditorInfoCompatUtils.imeOptionsName(
a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"),
textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"),
@@ -580,18 +613,18 @@ public class KeyboardParser {
return false;
}
- private boolean parseDefault(XmlResourceParser parser, Row row, List<Key> keys)
+ private boolean parseDefault(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException {
if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
if (row == null) {
- parseKeyboardContent(parser, keys);
+ parseKeyboardContent(parser, skip);
} else {
- parseRowContent(parser, row, keys);
+ parseRowContent(parser, row, skip);
}
return true;
}
- private void parseKeyStyle(XmlResourceParser parser, List<Key> keys) {
+ private void parseKeyStyle(XmlResourceParser parser, boolean skip) {
TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard_KeyStyle);
TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@@ -600,7 +633,7 @@ public class KeyboardParser {
if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
throw new ParseException("<" + TAG_KEY_STYLE
+ "/> needs styleName attribute", parser);
- if (keys != null)
+ if (!skip)
mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
} finally {
keyStyleAttr.recycle();
@@ -616,36 +649,45 @@ public class KeyboardParser {
}
private void startKeyboard() {
- mCurrentY += mKeyboardTopPadding;
+ mCurrentY += mParams.mTopPadding;
}
private void startRow(Row row) {
mCurrentX = 0;
- setSpacer(mCurrentX, mHorizontalEdgesPadding);
+ setSpacer(mCurrentX, mParams.mHorizontalEdgesPadding);
mCurrentRow = row;
+ mLeftEdge = true;
+ mRightEdgeKey = null;
}
private void endRow() {
if (mCurrentRow == null)
throw new InflateException("orphant end row tag");
- setSpacer(mCurrentX, mHorizontalEdgesPadding);
- if (mCurrentX > mMaxRowWidth)
- mMaxRowWidth = mCurrentX;
- mCurrentY += mCurrentRow.mDefaultHeight;
+ if (mRightEdgeKey != null) {
+ mRightEdgeKey.addEdgeFlags(Keyboard.EDGE_RIGHT);
+ mRightEdgeKey = null;
+ }
+ setSpacer(mCurrentX, mParams.mHorizontalEdgesPadding);
+ mCurrentY += mCurrentRow.mRowHeight;
mCurrentRow = null;
}
private void endKey(Key key) {
- mCurrentX = key.mX - key.mGap / 2 + key.mWidth + key.mGap;
+ mCurrentX = key.mX - key.mHorizontalGap / 2 + key.mWidth + key.mHorizontalGap;
+ if (mLeftEdge) {
+ key.addEdgeFlags(Keyboard.EDGE_LEFT);
+ mLeftEdge = false;
+ }
+ mRightEdgeKey = key;
}
- private void endKeyboard(int defaultVerticalGap) {
- mCurrentY += mKeyboardBottomPadding;
- mTotalHeight = mCurrentY - defaultVerticalGap;
+ private void endKeyboard() {
}
private void setSpacer(int keyXPos, int width) {
mCurrentX = keyXPos + width;
+ mLeftEdge = false;
+ mRightEdgeKey = null;
}
public static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
index 535a6954c..2d8b7bf11 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -21,7 +21,6 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.Log;
-import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.R;
public class KeyboardIconsSet {
@@ -31,44 +30,33 @@ public class KeyboardIconsSet {
// This should be aligned with Keyboard.keyIcon enum.
private static final int ICON_SHIFT_KEY = 1;
- private static final int ICON_TO_SYMBOL_KEY = 2;
- private static final int ICON_TO_SYMBOL_KEY_WITH_SHORTCUT = 3;
- private static final int ICON_DELETE_KEY = 4;
- private static final int ICON_DELETE_RTL_KEY = 5;
- private static final int ICON_SETTINGS_KEY = 6;
- private static final int ICON_SHORTCUT_KEY = 7;
- private static final int ICON_SPACE_KEY = 8;
- private static final int ICON_RETURN_KEY = 9;
- private static final int ICON_SEARCH_KEY = 10;
- private static final int ICON_TAB_KEY = 11;
+ private static final int ICON_DELETE_KEY = 2;
+ private static final int ICON_SETTINGS_KEY = 3; // This is also represented as "@icon/3" in XML.
+ private static final int ICON_SPACE_KEY = 4;
+ private static final int ICON_RETURN_KEY = 5;
+ private static final int ICON_SEARCH_KEY = 6;
+ private static final int ICON_TAB_KEY = 7; // This is also represented as "@icon/7" in XML.
+ private static final int ICON_SHORTCUT_KEY = 8;
+ private static final int ICON_SHORTCUT_FOR_LABEL = 9;
// This should be aligned with Keyboard.keyIconShifted enum.
- private static final int ICON_SHIFTED_SHIFT_KEY = 12;
+ private static final int ICON_SHIFTED_SHIFT_KEY = 10;
// This should be aligned with Keyboard.keyIconPreview enum.
- private static final int ICON_PREVIEW_SPACE_KEY = 13;
- private static final int ICON_PREVIEW_TAB_KEY = 14;
- private static final int ICON_PREVIEW_SETTINGS_KEY = 15;
- private static final int ICON_PREVIEW_SHORTCUT_KEY = 16;
+ private static final int ICON_PREVIEW_TAB_KEY = 11;
+ private static final int ICON_PREVIEW_SETTINGS_KEY = 12;
+ private static final int ICON_PREVIEW_SHORTCUT_KEY = 13;
- private static final int ICON_LAST = 16;
+ private static final int ICON_LAST = 13;
private final Drawable mIcons[] = new Drawable[ICON_LAST + 1];
- private static final int getIconId(int attrIndex) {
+ private static final int getIconId(final int attrIndex) {
switch (attrIndex) {
case R.styleable.Keyboard_iconShiftKey:
return ICON_SHIFT_KEY;
- case R.styleable.Keyboard_iconToSymbolKey:
- return ICON_TO_SYMBOL_KEY;
- case R.styleable.Keyboard_iconToSymbolKeyWithShortcut:
- return ICON_TO_SYMBOL_KEY_WITH_SHORTCUT;
case R.styleable.Keyboard_iconDeleteKey:
return ICON_DELETE_KEY;
- case R.styleable.Keyboard_iconDeleteRtlKey:
- return ICON_DELETE_RTL_KEY;
case R.styleable.Keyboard_iconSettingsKey:
return ICON_SETTINGS_KEY;
- case R.styleable.Keyboard_iconShortcutKey:
- return ICON_SHORTCUT_KEY;
case R.styleable.Keyboard_iconSpaceKey:
return ICON_SPACE_KEY;
case R.styleable.Keyboard_iconReturnKey:
@@ -77,10 +65,12 @@ public class KeyboardIconsSet {
return ICON_SEARCH_KEY;
case R.styleable.Keyboard_iconTabKey:
return ICON_TAB_KEY;
+ case R.styleable.Keyboard_iconShortcutKey:
+ return ICON_SHORTCUT_KEY;
+ case R.styleable.Keyboard_iconShortcutForLabel:
+ return ICON_SHORTCUT_FOR_LABEL;
case R.styleable.Keyboard_iconShiftedShiftKey:
return ICON_SHIFTED_SHIFT_KEY;
- case R.styleable.Keyboard_iconPreviewSpaceKey:
- return ICON_PREVIEW_SPACE_KEY;
case R.styleable.Keyboard_iconPreviewTabKey:
return ICON_PREVIEW_TAB_KEY;
case R.styleable.Keyboard_iconPreviewSettingsKey:
@@ -92,16 +82,14 @@ public class KeyboardIconsSet {
}
}
- public void loadIcons(TypedArray keyboardAttrs) {
+ public void loadIcons(final TypedArray keyboardAttrs) {
final int count = keyboardAttrs.getIndexCount();
for (int i = 0; i < count; i++) {
final int attrIndex = keyboardAttrs.getIndex(i);
final int iconId = getIconId(attrIndex);
if (iconId != ICON_UNDEFINED) {
try {
- final Drawable icon = keyboardAttrs.getDrawable(attrIndex);
- Keyboard.setDefaultBounds(icon);
- mIcons[iconId] = icon;
+ mIcons[iconId] = setDefaultBounds(keyboardAttrs.getDrawable(attrIndex));
} catch (Resources.NotFoundException e) {
Log.w(TAG, "Drawable resource for icon #" + iconId + " not found");
}
@@ -109,11 +97,18 @@ public class KeyboardIconsSet {
}
}
- public Drawable getIcon(int iconId) {
+ public Drawable getIcon(final int iconId) {
if (iconId == ICON_UNDEFINED)
return null;
if (iconId < 0 || iconId >= mIcons.length)
throw new IllegalArgumentException("icon id is out of range: " + iconId);
return mIcons[iconId];
}
+
+ private static Drawable setDefaultBounds(final Drawable icon) {
+ if (icon != null) {
+ icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
+ }
+ return icon;
+ }
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
new file mode 100644
index 000000000..4ccaa72d2
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -0,0 +1,95 @@
+/*
+ * 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.keyboard.internal;
+
+import android.graphics.drawable.Drawable;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class KeyboardParams {
+ public KeyboardId mId;
+
+ public int mOccupiedHeight;
+ public int mOccupiedWidth;
+
+ public int mHeight;
+ public int mWidth;
+
+ public int mTopPadding;
+ public int mBottomPadding;
+ public int mHorizontalEdgesPadding;
+ public int mHorizontalCenterPadding;
+
+ public int mDefaultRowHeight;
+ public int mDefaultKeyWidth;
+ public int mHorizontalGap;
+ public int mVerticalGap;
+
+ public boolean mIsRtlKeyboard;
+ public int mPopupKeyboardResId;
+ public int mMaxPopupColumn;
+
+ public int GRID_WIDTH;
+ public int GRID_HEIGHT;
+
+ public final List<Key> mKeys = new ArrayList<Key>();
+ public final List<Key> mShiftKeys = new ArrayList<Key>();
+ public final Set<Key> mShiftLockKeys = new HashSet<Key>();
+ public final Map<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>();
+ public final Map<Key, Drawable> mUnshiftedIcons = new HashMap<Key, Drawable>();
+ public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
+
+ public int mMostCommonKeyWidth = 0;
+
+ public void onAddKey(Key key) {
+ mKeys.add(key);
+ updateHistogram(key);
+ if (key.mCode == Keyboard.CODE_SHIFT) {
+ mShiftKeys.add(key);
+ if (key.mSticky) {
+ mShiftLockKeys.add(key);
+ }
+ }
+ }
+
+ public void addShiftedIcon(Key key, Drawable icon) {
+ mUnshiftedIcons.put(key, key.getIcon());
+ mShiftedIcons.put(key, icon);
+ }
+
+ private int mMaxCount = 0;
+ private final Map<Integer, Integer> mHistogram = new HashMap<Integer, Integer>();
+
+ private void updateHistogram(Key key) {
+ final Integer width = key.mWidth + key.mHorizontalGap;
+ final int count = (mHistogram.containsKey(width) ? mHistogram.get(width) : 0) + 1;
+ mHistogram.put(width, count);
+ if (count > mMaxCount) {
+ mMaxCount = count;
+ mMostCommonKeyWidth = width;
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
index cc89579bb..31a291cef 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
@@ -16,8 +16,6 @@
package com.android.inputmethod.keyboard.internal;
-import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -27,26 +25,30 @@ import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.MiniKeyboard;
import com.android.inputmethod.latin.R;
-import java.util.List;
-
-public class MiniKeyboardBuilder {
- private final Resources mRes;
- private final MiniKeyboard mKeyboard;
+public class MiniKeyboardBuilder extends
+ KeyboardBuilder<MiniKeyboardBuilder.MiniKeyboardParams> {
private final CharSequence[] mPopupCharacters;
- private final MiniKeyboardLayoutParams mParams;
- /* package */ static class MiniKeyboardLayoutParams {
- public final int mKeyWidth;
- public final int mRowHeight;
- /* package */ final int mTopRowAdjustment;
- public final int mNumRows;
- public final int mNumColumns;
- public final int mLeftKeys;
- public final int mRightKeys; // includes default key.
- public int mTopPadding;
+ public static class MiniKeyboardParams extends KeyboardParams {
+ /* package */ int mTopRowAdjustment;
+ public int mNumRows;
+ public int mNumColumns;
+ public int mLeftKeys;
+ public int mRightKeys; // includes default key.
+
+ public MiniKeyboardParams() {
+ super();
+ }
+
+ /* package for test */ MiniKeyboardParams(int numKeys, int maxColumns, int keyWidth,
+ int rowHeight, int coordXInParent, int parentKeyboardWidth) {
+ super();
+ setParameters(
+ numKeys, maxColumns, keyWidth, rowHeight, coordXInParent, parentKeyboardWidth);
+ }
/**
- * The object holding mini keyboard layout parameters.
+ * Set keyboard parameters of mini keyboard.
*
* @param numKeys number of keys in this mini keyboard.
* @param maxColumns number of maximum columns of this mini keyboard.
@@ -54,15 +56,15 @@ public class MiniKeyboardBuilder {
* @param rowHeight mini keyboard row height in pixel, including vertical gap.
* @param coordXInParent coordinate x of the popup key in parent keyboard.
* @param parentKeyboardWidth parent keyboard width in pixel.
- * parent keyboard.
*/
- public MiniKeyboardLayoutParams(int numKeys, int maxColumns, int keyWidth, int rowHeight,
+ public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight,
int coordXInParent, int parentKeyboardWidth) {
- if (parentKeyboardWidth / keyWidth < maxColumns)
+ if (parentKeyboardWidth / keyWidth < maxColumns) {
throw new IllegalArgumentException("Keyboard is too small to hold mini keyboard: "
+ parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
- mKeyWidth = keyWidth;
- mRowHeight = rowHeight;
+ }
+ mDefaultKeyWidth = keyWidth;
+ mDefaultRowHeight = rowHeight;
final int numRows = (numKeys + maxColumns - 1) / maxColumns;
mNumRows = numRows;
@@ -108,6 +110,9 @@ public class MiniKeyboardBuilder {
} else {
mTopRowAdjustment = -1;
}
+
+ mWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth;
+ mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
}
// Return key position according to column count (0 is default).
@@ -160,19 +165,19 @@ public class MiniKeyboardBuilder {
}
public int getDefaultKeyCoordX() {
- return mLeftKeys * mKeyWidth;
+ return mLeftKeys * mDefaultKeyWidth;
}
public int getX(int n, int row) {
- final int x = getColumnPos(n) * mKeyWidth + getDefaultKeyCoordX();
+ final int x = getColumnPos(n) * mDefaultKeyWidth + getDefaultKeyCoordX();
if (isTopRow(row)) {
- return x + mTopRowAdjustment * (mKeyWidth / 2);
+ return x + mTopRowAdjustment * (mDefaultKeyWidth / 2);
}
return x;
}
public int getY(int row) {
- return (mNumRows - 1 - row) * mRowHeight + mTopPadding;
+ return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
}
public int getRowFlags(int row) {
@@ -185,42 +190,32 @@ public class MiniKeyboardBuilder {
private boolean isTopRow(int rowCount) {
return rowCount == mNumRows - 1;
}
-
- public void setTopPadding (int topPadding) {
- mTopPadding = topPadding;
- }
-
- public int getKeyboardHeight() {
- return mNumRows * mRowHeight + mTopPadding;
- }
-
- public int getKeyboardWidth() {
- return mNumColumns * mKeyWidth;
- }
}
- public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey,
+ public MiniKeyboardBuilder(KeyboardView view, int xmlId, Key parentKey,
Keyboard parentKeyboard) {
- final Context context = view.getContext();
- mRes = context.getResources();
- final MiniKeyboard keyboard = new MiniKeyboard(
- context, layoutTemplateResId, parentKeyboard);
- mKeyboard = keyboard;
+ super(view.getContext(), new MiniKeyboardParams());
+ load(parentKeyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId));
+
+ // HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal
+ // and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are
+ // needed to keep having the same horizontal and vertical key spacing.
+ mParams.mHorizontalGap = 0;
+ mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
+ // TODO: When we have correctly padded key background 9-patch drawables for mini keyboard,
+ // revert the above hacks and uncomment the following lines.
+ //mParams.mHorizontalGap = parentKeyboard.mHorizontalGap;
+ //mParams.mVerticalGap = parentKeyboard.mVerticalGap;
+
+ mParams.mIsRtlKeyboard = parentKeyboard.mIsRtlKeyboard;
mPopupCharacters = parentKey.mPopupCharacters;
- final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth());
- final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
+ final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, mParams.mDefaultKeyWidth);
+ mParams.setParameters(
mPopupCharacters.length, parentKey.mMaxPopupColumn,
- keyWidth, parentKeyboard.getRowHeight(),
- parentKey.mX + (parentKey.mWidth + parentKey.mGap) / 2 - keyWidth / 2,
+ keyWidth, parentKeyboard.mDefaultRowHeight,
+ parentKey.mX + (mParams.mDefaultKeyWidth - keyWidth) / 2,
view.getMeasuredWidth());
- params.setTopPadding(keyboard.getVerticalGap());
- mParams = params;
-
- keyboard.setRowHeight(params.mRowHeight);
- keyboard.setHeight(params.getKeyboardHeight());
- keyboard.setMinWidth(params.getKeyboardWidth());
- keyboard.setDefaultCoordX(params.getDefaultKeyCoordX() + params.mKeyWidth / 2);
}
private static int getMaxKeyWidth(KeyboardView view, CharSequence[] popupCharacters,
@@ -249,17 +244,16 @@ public class MiniKeyboardBuilder {
return Math.max(minKeyWidth, maxWidth + horizontalPadding);
}
+ @Override
public MiniKeyboard build() {
- final MiniKeyboard keyboard = mKeyboard;
- final List<Key> keys = keyboard.getKeys();
- final MiniKeyboardLayoutParams params = mParams;
+ final MiniKeyboardParams params = mParams;
for (int n = 0; n < mPopupCharacters.length; n++) {
final CharSequence label = mPopupCharacters[n];
final int row = n / params.mNumColumns;
- final Key key = new Key(mRes, keyboard, label, params.getX(n, row), params.getY(row),
- params.mKeyWidth, params.mRowHeight, params.getRowFlags(row));
- keys.add(key);
+ final Key key = new Key(mResources, params, label, params.getX(n, row), params.getY(row),
+ params.mDefaultKeyWidth, params.mDefaultRowHeight, params.getRowFlags(row));
+ params.onAddKey(key);
}
- return keyboard;
+ return new MiniKeyboard(params);
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index 545b27fdc..55175e046 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -37,22 +37,21 @@ public class PointerTrackerQueue {
if (t.isModifier()) {
oldestPos++;
} else {
- t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime, true);
+ t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
queue.remove(oldestPos);
}
}
}
public void releaseAllPointers(long eventTime) {
- releaseAllPointersExcept(null, eventTime, true);
+ releaseAllPointersExcept(null, eventTime);
}
- public void releaseAllPointersExcept(PointerTracker tracker, long eventTime,
- boolean updateReleasedKeyGraphics) {
+ public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
for (PointerTracker t : mQueue) {
if (t == tracker)
continue;
- t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime, updateReleasedKeyGraphics);
+ t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
}
mQueue.clear();
if (tracker != null)
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
index 8276f5d78..032489e66 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
@@ -23,6 +23,8 @@ import android.util.Log;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.R;
+import java.util.ArrayList;
+
/**
* String parser of popupCharacters attribute of Key.
* The string is comma separated texts each of which represents one popup key.
@@ -182,4 +184,54 @@ public class PopupCharactersParser {
super(message);
}
}
+
+ public interface CodeFilter {
+ public boolean shouldFilterOut(int code);
+ }
+
+ public static final CodeFilter DIGIT_FILTER = new CodeFilter() {
+ @Override
+ public boolean shouldFilterOut(int code) {
+ return Character.isDigit(code);
+ }
+ };
+
+ public static final CodeFilter NON_ASCII_FILTER = new CodeFilter() {
+ @Override
+ public boolean shouldFilterOut(int code) {
+ return code < 0x20 || code > 0x7e;
+ }
+ };
+
+ public static CharSequence[] filterOut(Resources res, CharSequence[] popupCharacters,
+ CodeFilter filter) {
+ if (popupCharacters == null || popupCharacters.length < 1) {
+ return null;
+ }
+ if (popupCharacters.length == 1
+ && filter.shouldFilterOut(getCode(res, popupCharacters[0].toString()))) {
+ return null;
+ }
+ ArrayList<CharSequence> filtered = null;
+ for (int i = 0; i < popupCharacters.length; i++) {
+ final CharSequence popupSpec = popupCharacters[i];
+ if (filter.shouldFilterOut(getCode(res, popupSpec.toString()))) {
+ if (filtered == null) {
+ filtered = new ArrayList<CharSequence>();
+ for (int j = 0; j < i; j++) {
+ filtered.add(popupCharacters[j]);
+ }
+ }
+ } else if (filtered != null) {
+ filtered.add(popupSpec);
+ }
+ }
+ if (filtered == null) {
+ return popupCharacters;
+ }
+ if (filtered.size() == 0) {
+ return null;
+ }
+ return filtered.toArray(new CharSequence[filtered.size()]);
+ }
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/Row.java b/java/src/com/android/inputmethod/keyboard/internal/Row.java
index 06aadcc05..d53fe12e2 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/Row.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/Row.java
@@ -31,43 +31,19 @@ import com.android.inputmethod.latin.R;
*/
public class Row {
/** Default width of a key in this row. */
- public final int mDefaultWidth;
+ public final int mDefaultKeyWidth;
/** Default height of a key in this row. */
- public final int mDefaultHeight;
- /** Default horizontal gap between keys in this row. */
- public final int mDefaultHorizontalGap;
- /** Vertical gap following this row. */
- public final int mVerticalGap;
- /**
- * Edge flags for this row of keys. Possible values that can be assigned are
- * {@link Keyboard#EDGE_TOP EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM EDGE_BOTTOM}
- */
- public final int mRowEdgeFlags;
+ public final int mRowHeight;
- private final Keyboard mKeyboard;
-
- public Row(Resources res, Keyboard keyboard, XmlResourceParser parser) {
- this.mKeyboard = keyboard;
- final int keyboardWidth = keyboard.getDisplayWidth();
- final int keyboardHeight = keyboard.getKeyboardHeight();
+ public Row(Resources res, KeyboardParams params, XmlResourceParser parser) {
+ final int keyboardWidth = params.mWidth;
+ final int keyboardHeight = params.mHeight;
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard);
- mDefaultWidth = KeyboardParser.getDimensionOrFraction(a,
- R.styleable.Keyboard_keyWidth, keyboardWidth, keyboard.getKeyWidth());
- mDefaultHeight = KeyboardParser.getDimensionOrFraction(a,
- R.styleable.Keyboard_rowHeight, keyboardHeight, keyboard.getRowHeight());
- mDefaultHorizontalGap = KeyboardParser.getDimensionOrFraction(a,
- R.styleable.Keyboard_horizontalGap, keyboardWidth, keyboard.getHorizontalGap());
- mVerticalGap = KeyboardParser.getDimensionOrFraction(a,
- R.styleable.Keyboard_verticalGap, keyboardHeight, keyboard.getVerticalGap());
- a.recycle();
- a = res.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.Keyboard_Row);
- mRowEdgeFlags = a.getInt(R.styleable.Keyboard_Row_rowEdgeFlags, 0);
+ mDefaultKeyWidth = KeyboardBuilder.getDimensionOrFraction(a,
+ R.styleable.Keyboard_keyWidth, keyboardWidth, params.mDefaultKeyWidth);
+ mRowHeight = KeyboardBuilder.getDimensionOrFraction(a,
+ R.styleable.Keyboard_rowHeight, keyboardHeight, params.mDefaultRowHeight);
a.recycle();
}
-
- public Keyboard getKeyboard() {
- return mKeyboard;
- }
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java b/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java
deleted file mode 100644
index ef3ea4c12..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2010 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.keyboard.internal;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.text.TextPaint;
-import android.view.ViewConfiguration;
-
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.LatinKeyboard;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
-
-/**
- * Animation to be displayed on the spacebar preview popup when switching languages by swiping the
- * spacebar. It draws the current, previous and next languages and moves them by the delta of touch
- * movement on the spacebar.
- */
-public class SlidingLocaleDrawable extends Drawable {
- private static final int SLIDE_SPEED_MULTIPLIER_RATIO = 150;
- private final int mWidth;
- private final int mHeight;
- private final Drawable mBackground;
- private final int mSpacebarTextColor;
- private final TextPaint mTextPaint;
- private final int mMiddleX;
- private final boolean mDrawArrows;
- private final int mThreshold;
-
- private int mDiff;
- private boolean mHitThreshold;
- private String mCurrentLanguage;
- private String mNextLanguage;
- private String mPrevLanguage;
-
- public SlidingLocaleDrawable(Context context, Drawable background, int width, int height) {
- mBackground = background;
- Keyboard.setDefaultBounds(background);
- mWidth = width;
- mHeight = height;
- final TextPaint textPaint = new TextPaint();
- textPaint.setTextSize(LatinKeyboard.getTextSizeFromTheme(
- context.getTheme(), android.R.style.TextAppearance_Medium, 18));
- textPaint.setColor(Color.TRANSPARENT);
- textPaint.setAntiAlias(true);
- mTextPaint = textPaint;
- mMiddleX = (background != null) ? (mWidth - mBackground.getIntrinsicWidth()) / 2 : 0;
-
- final TypedArray a = context.obtainStyledAttributes(
- null, R.styleable.KeyboardView, R.attr.keyboardViewStyle, R.style.KeyboardView);
- mSpacebarTextColor = a.getColor(R.styleable.KeyboardView_keyPreviewTextColor, 0);
- final int spacebarPreviewBackrgound = a.getResourceId(
- R.styleable.KeyboardView_keyPreviewSpacebarBackground, 0);
- // If spacebar preview background is transparent, we need not draw arrows.
- mDrawArrows = (spacebarPreviewBackrgound != R.drawable.transparent);
- a.recycle();
-
- mThreshold = ViewConfiguration.get(context).getScaledTouchSlop();
- }
-
- public void setDiff(int diff) {
- if (diff == Integer.MAX_VALUE) {
- mHitThreshold = false;
- mCurrentLanguage = null;
- return;
- }
- mDiff = Math.max(diff, diff * SLIDE_SPEED_MULTIPLIER_RATIO / 100);
- if (mDiff > mWidth) mDiff = mWidth;
- if (mDiff < -mWidth) mDiff = -mWidth;
- if (Math.abs(mDiff) > mThreshold) mHitThreshold = true;
- invalidateSelf();
- }
-
-
- @Override
- public void draw(Canvas canvas) {
- canvas.save();
- if (mHitThreshold) {
- Paint paint = mTextPaint;
- final int width = mWidth;
- final int height = mHeight;
- final int diff = mDiff;
- canvas.clipRect(0, 0, width, height);
- if (mCurrentLanguage == null) {
- SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
- mCurrentLanguage = subtypeSwitcher.getInputLanguageName();
- mNextLanguage = subtypeSwitcher.getNextInputLanguageName();
- mPrevLanguage = subtypeSwitcher.getPreviousInputLanguageName();
- }
- // Draw language text.
- final float baseline = mHeight * LatinKeyboard.SPACEBAR_LANGUAGE_BASELINE
- - paint.descent();
- paint.setColor(mSpacebarTextColor);
- paint.setTextAlign(Align.CENTER);
- canvas.drawText(mCurrentLanguage, width / 2 + diff, baseline, paint);
- canvas.drawText(mNextLanguage, diff - width / 2, baseline, paint);
- canvas.drawText(mPrevLanguage, diff + width + width / 2, baseline, paint);
- if (mDrawArrows) {
- paint.setTextAlign(Align.LEFT);
- canvas.drawText(LatinKeyboard.ARROW_LEFT, 0, baseline, paint);
- paint.setTextAlign(Align.RIGHT);
- canvas.drawText(LatinKeyboard.ARROW_RIGHT, width, baseline, paint);
- }
- }
- if (mBackground != null) {
- canvas.translate(mMiddleX, 0);
- mBackground.draw(canvas);
- }
- canvas.restore();
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- @Override
- public void setAlpha(int alpha) {
- // Ignore
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- // Ignore
- }
-
- @Override
- public int getIntrinsicWidth() {
- return mWidth;
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mHeight;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 9748d6006..6a6a0a4ee 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -156,10 +156,11 @@ public class BinaryDictionary extends Dictionary {
}
}
+ // proximityInfo may not be null.
@Override
- public void getWords(final WordComposer codes, final WordCallback callback) {
- final int count = getSuggestions(codes, mKeyboardSwitcher.getLatinKeyboard(),
- mOutputChars, mScores);
+ public void getWords(final WordComposer codes, final WordCallback callback,
+ final ProximityInfo proximityInfo) {
+ final int count = getSuggestions(codes, proximityInfo, mOutputChars, mScores);
for (int j = 0; j < count; ++j) {
if (mScores[j] < 1) break;
@@ -179,8 +180,9 @@ public class BinaryDictionary extends Dictionary {
return mNativeDict != 0;
}
- /* package for test */ int getSuggestions(final WordComposer codes, final Keyboard keyboard,
- char[] outputChars, int[] scores) {
+ // proximityInfo may not be null.
+ /* package for test */ int getSuggestions(final WordComposer codes,
+ final ProximityInfo proximityInfo, char[] outputChars, int[] scores) {
if (!isValidDictionary()) return -1;
final int codesSize = codes.size();
@@ -196,9 +198,8 @@ public class BinaryDictionary extends Dictionary {
Arrays.fill(outputChars, (char) 0);
Arrays.fill(scores, 0);
- final int proximityInfo = keyboard == null ? 0 : keyboard.getProximityInfo();
return getSuggestionsNative(
- mNativeDict, proximityInfo,
+ mNativeDict, proximityInfo.getNativeProximityInfo(),
codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
mFlags, outputChars, scores);
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 76a230f82..f4ba0bcdc 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -19,8 +19,11 @@ package com.android.inputmethod.latin;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
+import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
@@ -28,7 +31,8 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -37,47 +41,111 @@ import java.util.Locale;
* file from the dictionary provider
*/
public class BinaryDictionaryFileDumper {
+ private static final String TAG = BinaryDictionaryFileDumper.class.getSimpleName();
+
/**
* The size of the temporary buffer to copy files.
*/
static final int FILE_READ_BUFFER_SIZE = 1024;
+ private static final String DICTIONARY_PROJECTION[] = { "id" };
+
// Prevents this class to be accidentally instantiated.
private BinaryDictionaryFileDumper() {
}
/**
- * Generates a file name that matches the locale passed as an argument.
- * The file name is basically the result of the .toString() method, except we replace
- * any @File.separator with an underscore to avoid generating a file name that may not
- * be created.
+ * Escapes a string for any characters that may be suspicious for a file or directory name.
+ *
+ * Concretely this does a sort of URL-encoding except it will encode everything that's not
+ * alphanumeric or underscore. (true URL-encoding leaves alone characters like '*', which
+ * we cannot allow here)
+ */
+ // TODO: create a unit test for this method
+ private static String replaceFileNameDangerousCharacters(String name) {
+ // This assumes '%' is fully available as a non-separator, normal
+ // character in a file name. This is probably true for all file systems.
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < name.length(); ++i) {
+ final int codePoint = name.codePointAt(i);
+ if (Character.isLetterOrDigit(codePoint) || '_' == codePoint) {
+ sb.appendCodePoint(codePoint);
+ } else {
+ sb.append('%');
+ sb.append(Integer.toHexString(codePoint));
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Find out the cache directory associated with a specific locale.
+ */
+ private static String getCacheDirectoryForLocale(Locale locale, Context context) {
+ final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale.toString());
+ final String absoluteDirectoryName = context.getFilesDir() + File.separator
+ + relativeDirectoryName;
+ final File directory = new File(absoluteDirectoryName);
+ if (!directory.exists()) {
+ if (!directory.mkdirs()) {
+ Log.e(TAG, "Could not create the directory for locale" + locale);
+ }
+ }
+ return absoluteDirectoryName;
+ }
+
+ /**
+ * Generates a file name for the id and locale passed as an argument.
+ *
+ * In the current implementation the file name returned will always be unique for
+ * any id/locale pair, but please do not expect that the id can be the same for
+ * different dictionaries with different locales. An id should be unique for any
+ * dictionary.
+ * The file name is pretty much an URL-encoded version of the id inside a directory
+ * named like the locale, except it will also escape characters that look dangerous
+ * to some file systems.
+ * @param id the id of the dictionary for which to get a file name
* @param locale the locale for which to get the file name
* @param context the context to use for getting the directory
* @return the name of the file to be created
*/
- private static String getCacheFileNameForLocale(Locale locale, Context context) {
- // The following assumes two things :
- // 1. That File.separator is not the same character as "_"
- // I don't think any android system will ever use "_" as a path separator
- // 2. That no two locales differ by only a File.separator versus a "_"
- // Since "_" can't be part of locale components this should be safe.
- // Examples:
- // en -> en
- // en_US_POSIX -> en_US_POSIX
- // en__foo/bar -> en__foo_bar
- final String[] separator = { File.separator };
- final String[] empty = { "_" };
- final CharSequence basename = TextUtils.replace(locale.toString(), separator, empty);
- return context.getFilesDir() + File.separator + basename;
+ private static String getCacheFileName(String id, Locale locale, Context context) {
+ final String fileName = replaceFileNameDangerousCharacters(id);
+ return getCacheDirectoryForLocale(locale, context) + File.separator + fileName;
}
/**
- * Return for a given locale the provider URI to query to get the dictionary.
+ * Return for a given locale or dictionary id the provider URI to get the dictionary.
*/
- public static Uri getProviderUri(Locale locale) {
+ private static Uri getProviderUri(String path) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath(
- locale.toString()).build();
+ path).build();
+ }
+
+ /**
+ * Queries a content provider for the list of dictionaries for a specific locale
+ * available to copy into Latin IME.
+ */
+ private static List<String> getDictIdList(final Locale locale, final Context context) {
+ final ContentResolver resolver = context.getContentResolver();
+ final Uri dictionaryPackUri = getProviderUri(locale.toString());
+
+ final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, null);
+ if (null == c) return Collections.<String>emptyList();
+ if (c.getCount() <= 0 || !c.moveToFirst()) {
+ c.close();
+ return Collections.<String>emptyList();
+ }
+
+ final List<String> list = new ArrayList<String>();
+ do {
+ final String id = c.getString(0);
+ if (TextUtils.isEmpty(id)) continue;
+ list.add(id);
+ } while (c.moveToNext());
+ c.close();
+ return list;
}
/**
@@ -94,31 +162,26 @@ public class BinaryDictionaryFileDumper {
* @throw FileNotFoundException if the provider returns non-existent data.
* @throw IOException if the provider-returned data could not be read.
*/
- public static List<AssetFileAddress> getDictSetFromContentProvider(Locale locale,
- Context context) throws FileNotFoundException, IOException {
- // TODO: check whether the dictionary is the same or not and if it is, return the cached
- // file.
- // TODO: This should be able to read a number of files from the dictionary pack, copy
- // them all and return them.
+ public static List<AssetFileAddress> getDictSetFromContentProvider(final Locale locale,
+ final Context context) throws FileNotFoundException, IOException {
final ContentResolver resolver = context.getContentResolver();
- final Uri dictionaryPackUri = getProviderUri(locale);
- final AssetFileDescriptor afd = resolver.openAssetFileDescriptor(dictionaryPackUri, "r");
- if (null == afd) return null;
- final String fileName =
- copyFileTo(afd.createInputStream(), getCacheFileNameForLocale(locale, context));
- return Arrays.asList(AssetFileAddress.makeFromFileName(fileName));
- }
-
- /**
- * Accepts a file as dictionary data for some locale and returns the name of a file.
- *
- * This will make the data in the input file the cached dictionary for this locale, overwriting
- * any previous cached data.
- */
- public static String getDictionaryFileFromFile(String fileName, Locale locale,
- Context context) throws FileNotFoundException, IOException {
- return copyFileTo(new FileInputStream(fileName), getCacheFileNameForLocale(locale,
- context));
+ final List<String> idList = getDictIdList(locale, context);
+ final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>();
+ for (String id : idList) {
+ final Uri wordListUri = getProviderUri(id);
+ final AssetFileDescriptor afd =
+ resolver.openAssetFileDescriptor(wordListUri, "r");
+ if (null == afd) continue;
+ final String fileName = copyFileTo(afd.createInputStream(),
+ getCacheFileName(id, locale, context));
+ afd.close();
+ if (0 >= resolver.delete(wordListUri, null, null)) {
+ // I'd rather not print the word list ID to the log here out of security concerns
+ Log.e(TAG, "Could not have the dictionary pack delete a word list");
+ }
+ fileAddressList.add(AssetFileAddress.makeFromFileName(fileName));
+ }
+ return fileAddressList;
}
/**
@@ -129,8 +192,11 @@ public class BinaryDictionaryFileDumper {
*/
public static String getDictionaryFileFromResource(int resource, Locale locale,
Context context) throws FileNotFoundException, IOException {
- return copyFileTo(context.getResources().openRawResource(resource),
- getCacheFileNameForLocale(locale, context));
+ final Resources res = context.getResources();
+ final Locale savedLocale = Utils.setSystemLocale(res, locale);
+ final InputStream stream = res.openRawResource(resource);
+ Utils.setSystemLocale(res, savedLocale);
+ return copyFileTo(stream, getCacheFileName(Integer.toString(resource), locale, context));
}
/**
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 7ce92920d..4b1c05adf 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
import android.util.Log;
import java.io.FileNotFoundException;
@@ -42,8 +43,13 @@ class BinaryDictionaryGetter {
/**
* Returns a file address from a resource, or null if it cannot be opened.
*/
- private static AssetFileAddress loadFallbackResource(Context context, int fallbackResId) {
- final AssetFileDescriptor afd = context.getResources().openRawResourceFd(fallbackResId);
+ private static AssetFileAddress loadFallbackResource(final Context context,
+ final int fallbackResId, final Locale locale) {
+ final Resources res = context.getResources();
+ final Locale savedLocale = Utils.setSystemLocale(res, locale);
+ final AssetFileDescriptor afd = res.openRawResourceFd(fallbackResId);
+ Utils.setSystemLocale(res, savedLocale);
+
if (afd == null) {
Log.e(TAG, "Found the resource but cannot read it. Is it compressed? resId="
+ fallbackResId);
@@ -57,9 +63,6 @@ class BinaryDictionaryGetter {
* Returns a list of file addresses for a given locale, trying relevant methods in order.
*
* Tries to get binary dictionaries from various sources, in order:
- * - Uses a private method of getting a private dictionaries, as implemented by the
- * PrivateBinaryDictionaryGetter class.
- * If that fails:
* - Uses a content provider to get a public dictionary set, as per the protocol described
* in BinaryDictionaryFileDumper.
* If that fails:
@@ -70,28 +73,23 @@ class BinaryDictionaryGetter {
*/
public static List<AssetFileAddress> getDictionaryFiles(Locale locale, Context context,
int fallbackResId) {
- // Try first to query a private package signed the same way for private files.
- final List<AssetFileAddress> privateFiles =
- PrivateBinaryDictionaryGetter.getDictionaryFiles(locale, context);
- if (null != privateFiles) {
- return privateFiles;
- } else {
- try {
- // If that was no-go, try to find a publicly exported dictionary.
- List<AssetFileAddress> listFromContentProvider =
- BinaryDictionaryFileDumper.getDictSetFromContentProvider(locale, context);
- if (null != listFromContentProvider) {
- return listFromContentProvider;
- }
- // If the list is null, fall through and return the fallback
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Unable to create dictionary file from provider for locale "
- + locale.toString() + ": falling back to internal dictionary");
- } catch (IOException e) {
- Log.e(TAG, "Unable to read source data for locale "
- + locale.toString() + ": falling back to internal dictionary");
+ try {
+ List<AssetFileAddress> listFromContentProvider =
+ BinaryDictionaryFileDumper.getDictSetFromContentProvider(locale, context);
+ if (null != listFromContentProvider) {
+ return listFromContentProvider;
}
- return Arrays.asList(loadFallbackResource(context, fallbackResId));
+ // If the list is null, fall through and return the fallback
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Unable to create dictionary file from provider for locale "
+ + locale.toString() + ": falling back to internal dictionary");
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to read source data for locale "
+ + locale.toString() + ": falling back to internal dictionary");
}
+ final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId,
+ locale);
+ if (null == fallbackAsset) return null;
+ return Arrays.asList(fallbackAsset);
}
}
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index 96225f2e9..d779c8565 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -52,14 +52,11 @@ import java.util.ArrayList;
import java.util.List;
public class CandidateView extends LinearLayout implements OnClickListener, OnLongClickListener {
-
public interface Listener {
public boolean addWordToDictionary(String word);
public void pickSuggestionManually(int index, CharSequence word);
}
- private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
- private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
// The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
private static final int MAX_SUGGESTIONS = 18;
private static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -68,11 +65,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
private static final boolean DBG = LatinImeLogger.sDBG;
private final ViewGroup mCandidatesStrip;
- private final int mCandidateCountInStrip;
- private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3;
- private final ViewGroup mCandidatesPaneControl;
- private final TextView mExpandCandidatesPane;
- private final TextView mCloseCandidatesPane;
private ViewGroup mCandidatesPane;
private ViewGroup mCandidatesPaneContainer;
private View mKeyboardView;
@@ -81,17 +73,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
private final ArrayList<TextView> mInfos = new ArrayList<TextView>();
private final ArrayList<View> mDividers = new ArrayList<View>();
- private final int mCandidateStripHeight;
- private final CharacterStyle mInvertedForegroundColorSpan;
- private final CharacterStyle mInvertedBackgroundColorSpan;
- private final int mAutoCorrectHighlight;
- private static final int AUTO_CORRECT_BOLD = 0x01;
- private static final int AUTO_CORRECT_UNDERLINE = 0x02;
- private static final int AUTO_CORRECT_INVERT = 0x04;
- private final int mColorTypedWord;
- private final int mColorAutoCorrect;
- private final int mColorSuggestedCandidate;
-
private final PopupWindow mPreviewPopup;
private final TextView mPreviewText;
@@ -103,9 +84,9 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
private boolean mShowingAutoCorrectionInverted;
private boolean mShowingAddToDictionary;
- private final CandidateViewLayoutParams mParams;
- private static final int PUNCTUATIONS_IN_STRIP = 6;
- private static final float MIN_TEXT_XSCALE = 0.75f;
+ private final SuggestionsStripParams mStripParams;
+ private final SuggestionsPaneParams mPaneParams;
+ private static final float MIN_TEXT_XSCALE = 0.70f;
private final UiHandler mHandler = new UiHandler(this);
@@ -158,118 +139,355 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
}
- private static class CandidateViewLayoutParams {
- public final TextPaint mPaint;
+ private static class CandidateViewParams {
public final int mPadding;
public final int mDividerWidth;
public final int mDividerHeight;
- public final int mControlWidth;
- private final int mAutoCorrectHighlight;
+ public final int mCandidateStripHeight;
- public final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
+ protected final List<TextView> mWords;
+ protected final List<View> mDividers;
+ protected final List<TextView> mInfos;
- public int mCountInStrip;
- // True if the mCountInStrip suggestions can fit in suggestion strip in equally divided
- // width without squeezing the text.
- public boolean mCanUseFixedWidthColumns;
- public int mMaxWidth;
- public int mAvailableWidthForWords;
- public int mConstantWidthForPaddings;
- public int mVariableWidthForWords;
- public float mScaleX;
+ protected CandidateViewParams(List<TextView> words, List<View> dividers,
+ List<TextView> infos) {
+ mWords = words;
+ mDividers = dividers;
+ mInfos = infos;
- public CandidateViewLayoutParams(Resources res, TextView word, View divider, View control,
- int autoCorrectHighlight) {
- mPaint = new TextPaint();
- final float textSize = res.getDimension(R.dimen.candidate_text_size);
- mPaint.setTextSize(textSize);
+ final TextView word = words.get(0);
+ final View divider = dividers.get(0);
mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight();
divider.measure(WRAP_CONTENT, MATCH_PARENT);
mDividerWidth = divider.getMeasuredWidth();
mDividerHeight = divider.getMeasuredHeight();
- mControlWidth = control.getMeasuredWidth();
- mAutoCorrectHighlight = autoCorrectHighlight;
+
+ final Resources res = word.getResources();
+ mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height);
}
+ }
- public void layoutStrip(SuggestedWords suggestions, int maxWidth, int maxCount) {
- final int size = suggestions.size();
- if (size == 0) return;
- setupTexts(suggestions, size, mAutoCorrectHighlight);
- mCountInStrip = Math.min(maxCount, size);
- mScaleX = 1.0f;
+ private static class SuggestionsPaneParams extends CandidateViewParams {
+ public SuggestionsPaneParams(List<TextView> words, List<View> dividers,
+ List<TextView> infos) {
+ super(words, dividers, infos);
+ }
+
+ public int layout(SuggestedWords suggestions, ViewGroup paneView, int from, int textColor,
+ int paneWidth) {
+ final int count = Math.min(mWords.size(), suggestions.size());
+ View centeringFrom = null, lastView = null;
+ int x = 0, y = 0;
+ for (int index = from; index < count; index++) {
+ final int pos = index;
+ final TextView word = mWords.get(pos);
+ final View divider = mDividers.get(pos);
+ final TextPaint paint = word.getPaint();
+ word.setTextColor(textColor);
+ final CharSequence styled = suggestions.getWord(pos);
+
+ final TextView info;
+ if (DBG) {
+ final CharSequence debugInfo = getDebugInfo(suggestions, index);
+ if (debugInfo != null) {
+ info = mInfos.get(index);
+ info.setText(debugInfo);
+ } else {
+ info = null;
+ }
+ } else {
+ info = null;
+ }
- do {
- mMaxWidth = maxWidth;
- if (size > mCountInStrip) {
- mMaxWidth -= mControlWidth;
+ final CharSequence text;
+ final float scaleX;
+ paint.setTextScaleX(1.0f);
+ final int textWidth = getTextWidth(styled, paint);
+ int available = paneWidth - x - mPadding;
+ if (textWidth >= available) {
+ // Needs new row, centering previous row.
+ centeringCandidates(paneView, centeringFrom, lastView, x, paneWidth);
+ x = 0;
+ y += mCandidateStripHeight;
+ }
+ if (x != 0) {
+ // Add divider if this isn't the left most suggestion in current row.
+ paneView.addView(divider);
+ FrameLayoutCompatUtils.placeViewAt(divider, x, y
+ + (mCandidateStripHeight - mDividerHeight) / 2, mDividerWidth,
+ mDividerHeight);
+ x += mDividerWidth;
+ }
+ available = paneWidth - x - mPadding;
+ text = getEllipsizedText(styled, available, paint);
+ scaleX = paint.getTextScaleX();
+ word.setText(text);
+ word.setTextScaleX(scaleX);
+ paneView.addView(word);
+ lastView = word;
+ if (x == 0)
+ centeringFrom = word;
+ word.measure(WRAP_CONTENT,
+ MeasureSpec.makeMeasureSpec(mCandidateStripHeight, MeasureSpec.EXACTLY));
+ final int width = word.getMeasuredWidth();
+ final int height = word.getMeasuredHeight();
+ FrameLayoutCompatUtils.placeViewAt(word, x, y + (mCandidateStripHeight - height)
+ / 2, width, height);
+ x += width;
+ if (info != null) {
+ paneView.addView(info);
+ lastView = info;
+ info.measure(WRAP_CONTENT, WRAP_CONTENT);
+ final int infoWidth = info.getMeasuredWidth();
+ FrameLayoutCompatUtils.placeViewAt(info, x - infoWidth, y, infoWidth,
+ info.getMeasuredHeight());
}
+ }
+ if (x != 0) {
+ // Centering last candidates row.
+ centeringCandidates(paneView, centeringFrom, lastView, x, paneWidth);
+ }
+
+ return count - from;
+ }
+ }
+
+ private static class SuggestionsStripParams extends CandidateViewParams {
+ private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3;
+ private static final int DEFAULT_CENTER_CANDIDATE_PERCENTILE = 40;
+ private static final int PUNCTUATIONS_IN_STRIP = 6;
+
+ private final int mColorTypedWord;
+ private final int mColorAutoCorrect;
+ private final int mColorSuggestedCandidate;
+ private final int mCandidateCountInStrip;
+ private final float mCenterCandidateWeight;
+ private final int mCenterCandidateIndex;
+ private final Drawable mMoreCandidateHint;
+
+ private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
+ private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
+ private final CharacterStyle mInvertedForegroundColorSpan;
+ private final CharacterStyle mInvertedBackgroundColorSpan;
+ private static final int AUTO_CORRECT_BOLD = 0x01;
+ private static final int AUTO_CORRECT_UNDERLINE = 0x02;
+ private static final int AUTO_CORRECT_INVERT = 0x04;
+
+ private final TextPaint mPaint;
+ private final int mAutoCorrectHighlight;
+
+ private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
+
+ public boolean mMoreSuggestionsAvailable;
+
+ public SuggestionsStripParams(Context context, AttributeSet attrs, int defStyle,
+ List<TextView> words, List<View> dividers, List<TextView> infos) {
+ super(words, dividers, infos);
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle);
+ mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0);
+ mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0);
+ mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0);
+ mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0);
+ mCandidateCountInStrip = a.getInt(
+ R.styleable.CandidateView_candidateCountInStrip,
+ DEFAULT_CANDIDATE_COUNT_IN_STRIP);
+ mCenterCandidateWeight = a.getInt(
+ R.styleable.CandidateView_centerCandidatePercentile,
+ DEFAULT_CENTER_CANDIDATE_PERCENTILE) / 100.0f;
+ a.recycle();
+
+ mCenterCandidateIndex = mCandidateCountInStrip / 2;
+ final Resources res = context.getResources();
+ mMoreCandidateHint = res.getDrawable(R.drawable.more_suggestions_hint);
+
+ mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff);
+ mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord);
- tryLayout();
+ mPaint = new TextPaint();
+ final float textSize = res.getDimension(R.dimen.candidate_text_size);
+ mPaint.setTextSize(textSize);
+ }
+
+ public int getTextColor() {
+ return mColorTypedWord;
+ }
+
+ private CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect) {
+ if (!isAutoCorrect)
+ return word;
+ final int len = word.length();
+ final Spannable spannedWord = new SpannableString(word);
+ if ((mAutoCorrectHighlight & AUTO_CORRECT_BOLD) != 0)
+ spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ if ((mAutoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0)
+ spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ return spannedWord;
+ }
+
+ private static boolean willAutoCorrect(SuggestedWords suggestions) {
+ return !suggestions.mTypedWordValid && suggestions.mHasMinimalSuggestion;
+ }
+
+ private int getWordPosition(int index, SuggestedWords suggestions) {
+ // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
+ // suggestions.
+ final int centerPos = willAutoCorrect(suggestions) ? 1 : 0;
+ if (index == mCenterCandidateIndex) {
+ return centerPos;
+ } else if (index == centerPos) {
+ return mCenterCandidateIndex;
+ } else {
+ return index;
+ }
+ }
+
+ private int getCandidateTextColor(int index, SuggestedWords suggestions, int pos) {
+ // TODO: Need to revisit this logic with bigram suggestions
+ final boolean isSuggestedCandidate = (pos != 0);
+
+ final int color;
+ if (index == mCenterCandidateIndex && willAutoCorrect(suggestions)) {
+ color = mColorAutoCorrect;
+ } else if (isSuggestedCandidate) {
+ color = mColorSuggestedCandidate;
+ } else {
+ color = mColorTypedWord;
+ }
+
+ final SuggestedWordInfo info = (pos < suggestions.size())
+ ? suggestions.getInfo(pos) : null;
+ if (info != null && info.isPreviousSuggestedWord()) {
+ return applyAlpha(color, 0.5f);
+ } else {
+ return color;
+ }
+ }
+
+ private static int applyAlpha(final int color, final float alpha) {
+ final int newAlpha = (int)(Color.alpha(color) * alpha);
+ return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color));
+ }
+
+ public CharSequence getInvertedText(CharSequence text) {
+ if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0)
+ return null;
+ final int len = text.length();
+ final Spannable word = new SpannableString(text);
+ word.setSpan(mInvertedBackgroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ word.setSpan(mInvertedForegroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ return word;
+ }
+
+ public int layout(SuggestedWords suggestions, ViewGroup stripView, ViewGroup paneView,
+ int stripWidth) {
+ if (suggestions.isPunctuationSuggestions()) {
+ return layoutPunctuationSuggestions(suggestions, stripView);
+ }
+
+ final int countInStrip = mCandidateCountInStrip;
+ setupTexts(suggestions, countInStrip);
+ mMoreSuggestionsAvailable = (suggestions.size() > countInStrip);
+ int x = 0;
+ for (int index = 0; index < countInStrip; index++) {
+ final int pos = getWordPosition(index, suggestions);
- if (mCanUseFixedWidthColumns) {
- return;
+ if (index != 0) {
+ final View divider = mDividers.get(pos);
+ // Add divider if this isn't the left most suggestion in candidate strip.
+ stripView.addView(divider);
}
- if (mVariableWidthForWords <= mAvailableWidthForWords) {
- return;
+
+ final CharSequence styled = mTexts.get(pos);
+ final TextView word = mWords.get(pos);
+ if (index == mCenterCandidateIndex && mMoreSuggestionsAvailable) {
+ // TODO: This "more suggestions hint" should have nicely designed icon.
+ word.setCompoundDrawablesWithIntrinsicBounds(
+ null, null, null, mMoreCandidateHint);
+ } else {
+ word.setCompoundDrawables(null, null, null, null);
}
- final float scaleX = mAvailableWidthForWords / (float)mVariableWidthForWords;
- if (scaleX >= MIN_TEXT_XSCALE) {
- mScaleX = scaleX;
- return;
+ // Disable this candidate if the suggestion is null or empty.
+ word.setEnabled(!TextUtils.isEmpty(styled));
+ word.setTextColor(getCandidateTextColor(index, suggestions, pos));
+ final int width = getCandidateWidth(index, stripWidth);
+ final CharSequence text = getEllipsizedText(styled, width, word.getPaint());
+ final float scaleX = word.getTextScaleX();
+ word.setText(text); // TextView.setText() resets text scale x to 1.0.
+ word.setTextScaleX(scaleX);
+ stripView.addView(word);
+ setLayoutWeight(word, getCandidateWeight(index), mCandidateStripHeight);
+
+ if (DBG) {
+ final CharSequence debugInfo = getDebugInfo(suggestions, pos);
+ if (debugInfo != null) {
+ final TextView info = mInfos.get(pos);
+ info.setText(debugInfo);
+ paneView.addView(info);
+ info.measure(WRAP_CONTENT, WRAP_CONTENT);
+ final int infoWidth = info.getMeasuredWidth();
+ final int y = info.getMeasuredHeight();
+ FrameLayoutCompatUtils.placeViewAt(info, x, 0, infoWidth, y);
+ x += infoWidth * 2;
+ }
}
+ }
- mCountInStrip--;
- } while (mCountInStrip > 1);
- }
-
- public void tryLayout() {
- final int maxCount = mCountInStrip;
- final int dividers = mDividerWidth * (maxCount - 1);
- mConstantWidthForPaddings = dividers + mPadding * maxCount;
- mAvailableWidthForWords = mMaxWidth - mConstantWidthForPaddings;
-
- mPaint.setTextScaleX(mScaleX);
- final int maxFixedWidthForWord = (mMaxWidth - dividers) / maxCount - mPadding;
- mCanUseFixedWidthColumns = true;
- mVariableWidthForWords = 0;
- for (int i = 0; i < maxCount; i++) {
- final int width = getTextWidth(mTexts.get(i), mPaint);
- if (width > maxFixedWidthForWord)
- mCanUseFixedWidthColumns = false;
- mVariableWidthForWords += width;
+ return countInStrip;
+ }
+
+ private int getCandidateWidth(int index, int maxWidth) {
+ final int paddings = mPadding * mCandidateCountInStrip;
+ final int dividers = mDividerWidth * (mCandidateCountInStrip - 1);
+ final int availableWidth = maxWidth - paddings - dividers;
+ return (int)(availableWidth * getCandidateWeight(index));
+ }
+
+ private float getCandidateWeight(int index) {
+ if (index == mCenterCandidateIndex) {
+ return mCenterCandidateWeight;
+ } else {
+ // TODO: Revisit this for cases of 5 or more suggestions
+ return (1.0f - mCenterCandidateWeight) / (mCandidateCountInStrip - 1);
}
}
- private void setupTexts(SuggestedWords suggestions, int count, int autoCorrectHighlight) {
+ private void setupTexts(SuggestedWords suggestions, int countInStrip) {
mTexts.clear();
- for (int i = 0; i < count; i++) {
- final CharSequence suggestion = suggestions.getWord(i);
- if (suggestion == null) {
- // Skip an empty suggestion, but we need to add a place-holder for it in order
- // to avoid an exception in the loop in updateSuggestions().
- mTexts.add("");
- continue;
- }
-
- final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion
- && ((i == 1 && !suggestions.mTypedWordValid)
- || (i == 0 && suggestions.mTypedWordValid));
- // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1
- // and there are multiple suggestions, such as the default punctuation list.
- // TODO: Need to revisit this logic with bigram suggestions
- final CharSequence styled = getStyledCandidateWord(suggestion, isAutoCorrect,
- autoCorrectHighlight);
+ final int count = Math.min(suggestions.size(), countInStrip);
+ for (int pos = 0; pos < count; pos++) {
+ final CharSequence word = suggestions.getWord(pos);
+ final boolean isAutoCorrect = pos == 1 && willAutoCorrect(suggestions);
+ final CharSequence styled = getStyledCandidateWord(word, isAutoCorrect);
mTexts.add(styled);
}
+ for (int pos = count; pos < countInStrip; pos++) {
+ // Make this inactive for touches in layout().
+ mTexts.add(null);
+ }
}
- @Override
- public String toString() {
- return String.format(
- "count=%d width=%d avail=%d fixcol=%s scaleX=%4.2f const=%d var=%d",
- mCountInStrip, mMaxWidth, mAvailableWidthForWords, mCanUseFixedWidthColumns,
- mScaleX, mConstantWidthForPaddings, mVariableWidthForWords);
+ private int layoutPunctuationSuggestions(SuggestedWords suggestions, ViewGroup stripView) {
+ final int countInStrip = Math.min(suggestions.size(), PUNCTUATIONS_IN_STRIP);
+ for (int index = 0; index < countInStrip; index++) {
+ if (index != 0) {
+ // Add divider if this isn't the left most suggestion in candidate strip.
+ stripView.addView(mDividers.get(index));
+ }
+
+ final TextView word = mWords.get(index);
+ word.setEnabled(true);
+ word.setTextColor(mColorTypedWord);
+ final CharSequence text = suggestions.getWord(index);
+ word.setText(text);
+ word.setTextScaleX(1.0f);
+ word.setCompoundDrawables(null, null, null, null);
+ stripView.addView(word);
+ setLayoutWeight(word, 1.0f, mCandidateStripHeight);
+ }
+ mMoreSuggestionsAvailable = false;
+ return countInStrip;
}
}
@@ -296,18 +514,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
setBackgroundDrawable(LinearLayoutCompatUtils.getBackgroundDrawable(
context, attrs, defStyle, R.style.CandidateViewStyle));
- final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle);
- mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0);
- mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0);
- mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0);
- mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0);
- mCandidateCountInStrip = a.getInt(
- R.styleable.CandidateView_candidateCountInStrip, DEFAULT_CANDIDATE_COUNT_IN_STRIP);
- a.recycle();
-
- Resources res = context.getResources();
- LayoutInflater inflater = LayoutInflater.from(context);
+ final LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.candidates_strip, this);
mPreviewPopup = new PopupWindow(context);
@@ -318,58 +525,26 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
mPreviewPopup.setBackgroundDrawable(null);
mCandidatesStrip = (ViewGroup)findViewById(R.id.candidates_strip);
- mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height);
- for (int i = 0; i < MAX_SUGGESTIONS; i++) {
+ for (int pos = 0; pos < MAX_SUGGESTIONS; pos++) {
final TextView word = (TextView)inflater.inflate(R.layout.candidate_word, null);
- word.setTag(i);
+ word.setTag(pos);
word.setOnClickListener(this);
- if (i == 0)
- word.setOnLongClickListener(this);
+ word.setOnLongClickListener(this);
mWords.add(word);
+ final View divider = inflater.inflate(R.layout.candidate_divider, null);
+ divider.setTag(pos);
+ divider.setOnClickListener(this);
+ mDividers.add(divider);
mInfos.add((TextView)inflater.inflate(R.layout.candidate_info, null));
- mDividers.add(inflater.inflate(R.layout.candidate_divider, null));
}
mTouchToSave = findViewById(R.id.touch_to_save);
mWordToSave = (TextView)findViewById(R.id.word_to_save);
mWordToSave.setOnClickListener(this);
- mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff);
- mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord);
-
- final TypedArray keyboardViewAttr = context.obtainStyledAttributes(
- attrs, R.styleable.KeyboardView, R.attr.keyboardViewStyle, R.style.KeyboardView);
- final Drawable expandBackground = keyboardViewAttr.getDrawable(
- R.styleable.KeyboardView_keyBackground);
- final Drawable closeBackground = keyboardViewAttr.getDrawable(
- R.styleable.KeyboardView_keyBackground);
- final int keyTextColor = keyboardViewAttr.getColor(
- R.styleable.KeyboardView_keyTextColor, 0xFF000000);
- keyboardViewAttr.recycle();
-
- mCandidatesPaneControl = (ViewGroup)findViewById(R.id.candidates_pane_control);
- mExpandCandidatesPane = (TextView)findViewById(R.id.expand_candidates_pane);
- mExpandCandidatesPane.setBackgroundDrawable(expandBackground);
- mExpandCandidatesPane.setTextColor(keyTextColor);
- mExpandCandidatesPane.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- expandCandidatesPane();
- }
- });
- mCloseCandidatesPane = (TextView)findViewById(R.id.close_candidates_pane);
- mCloseCandidatesPane.setBackgroundDrawable(closeBackground);
- mCloseCandidatesPane.setTextColor(keyTextColor);
- mCloseCandidatesPane.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- closeCandidatesPane();
- }
- });
- mCandidatesPaneControl.measure(WRAP_CONTENT, WRAP_CONTENT);
-
- mParams = new CandidateViewLayoutParams(res,
- mWords.get(0), mDividers.get(0), mCandidatesPaneControl, mAutoCorrectHighlight);
+ mStripParams = new SuggestionsStripParams(context, attrs, defStyle, mWords, mDividers,
+ mInfos);
+ mPaneParams = new SuggestionsPaneParams(mWords, mDividers, mInfos);
}
/**
@@ -390,7 +565,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
if (suggestions == null)
return;
mSuggestions = suggestions;
- mExpandCandidatesPane.setEnabled(false);
if (mShowingAutoCorrectionInverted) {
mHandler.postUpdateSuggestions();
} else {
@@ -398,181 +572,30 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
}
- private static CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect,
- int autoCorrectHighlight) {
- if (!isAutoCorrect)
- return word;
- final Spannable spannedWord = new SpannableString(word);
- if ((autoCorrectHighlight & AUTO_CORRECT_BOLD) != 0)
- spannedWord.setSpan(BOLD_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- if ((autoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0)
- spannedWord.setSpan(UNDERLINE_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- return spannedWord;
- }
-
- private int getCandidateTextColor(boolean isAutoCorrect, boolean isSuggestedCandidate,
- SuggestedWordInfo info) {
- final int color;
- if (isAutoCorrect) {
- color = mColorAutoCorrect;
- } else if (isSuggestedCandidate) {
- color = mColorSuggestedCandidate;
- } else {
- color = mColorTypedWord;
- }
- if (info != null && info.isPreviousSuggestedWord()) {
- final int newAlpha = (int)(Color.alpha(color) * 0.5f);
- return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color));
- } else {
- return color;
- }
- }
-
private void updateSuggestions() {
- final SuggestedWords suggestions = mSuggestions;
- final List<SuggestedWordInfo> suggestedWordInfoList = suggestions.mSuggestedWordInfoList;
- final int paneWidth = getWidth();
- final CandidateViewLayoutParams params = mParams;
-
clear();
closeCandidatesPane();
- if (suggestions.size() == 0)
+ if (mSuggestions.size() == 0)
return;
- params.layoutStrip(suggestions, paneWidth, suggestions.isPunctuationSuggestions()
- ? PUNCTUATIONS_IN_STRIP : mCandidateCountInStrip);
-
- final int count = Math.min(mWords.size(), suggestions.size());
- if (count <= params.mCountInStrip && !DBG) {
- mCandidatesPaneControl.setVisibility(GONE);
- } else {
- mCandidatesPaneControl.setVisibility(VISIBLE);
- mExpandCandidatesPane.setVisibility(VISIBLE);
- mExpandCandidatesPane.setEnabled(true);
- }
-
- final int countInStrip = params.mCountInStrip;
- View centeringFrom = null, lastView = null;
- int x = 0, y = 0, infoX = 0;
- for (int i = 0; i < count; i++) {
- final int pos;
- if (i <= 1) {
- final boolean willAutoCorrect = !suggestions.mTypedWordValid
- && suggestions.mHasMinimalSuggestion;
- pos = willAutoCorrect ? 1 - i : i;
- } else {
- pos = i;
- }
- final CharSequence suggestion = suggestions.getWord(pos);
- if (suggestion == null) continue;
-
- final SuggestedWordInfo suggestionInfo = (suggestedWordInfoList != null)
- ? suggestedWordInfoList.get(pos) : null;
- final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion
- && ((pos == 1 && !suggestions.mTypedWordValid)
- || (pos == 0 && suggestions.mTypedWordValid));
- // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1
- // and there are multiple suggestions, such as the default punctuation list.
- // TODO: Need to revisit this logic with bigram suggestions
- final boolean isSuggestedCandidate = (pos != 0);
- final boolean isPunctuationSuggestions = (suggestion.length() == 1 && count > 1);
-
- final TextView word = mWords.get(pos);
- final TextPaint paint = word.getPaint();
- // TODO: Reorder candidates in strip as appropriate. The center candidate should hold
- // the word when space is typed (valid typed word or auto corrected word).
- word.setTextColor(getCandidateTextColor(isAutoCorrect,
- isSuggestedCandidate || isPunctuationSuggestions, suggestionInfo));
- final CharSequence styled = params.mTexts.get(pos);
-
- final TextView info;
- if (DBG && suggestionInfo != null
- && !TextUtils.isEmpty(suggestionInfo.getDebugString())) {
- info = mInfos.get(i);
- info.setText(suggestionInfo.getDebugString());
- } else {
- info = null;
- }
+ final int width = getWidth();
+ final int countInStrip = mStripParams.layout(
+ mSuggestions, mCandidatesStrip, mCandidatesPane, width);
+ final int countInPane = mPaneParams.layout(
+ mSuggestions, mCandidatesPane, countInStrip, mStripParams.getTextColor(), width);
+ }
- final CharSequence text;
- final float scaleX;
- if (i < countInStrip) {
- if (i == 0 && params.mCountInStrip == 1) {
- text = getEllipsizedText(styled, params.mMaxWidth, paint);
- scaleX = paint.getTextScaleX();
- } else {
- text = styled;
- scaleX = params.mScaleX;
- }
- word.setText(text);
- word.setTextScaleX(scaleX);
- if (i != 0) {
- // Add divider if this isn't the left most suggestion in candidate strip.
- mCandidatesStrip.addView(mDividers.get(i));
- }
- mCandidatesStrip.addView(word);
- if (params.mCanUseFixedWidthColumns) {
- setLayoutWeight(word, 1.0f, mCandidateStripHeight);
- } else {
- final int width = getTextWidth(text, paint) + params.mPadding;
- setLayoutWeight(word, width, mCandidateStripHeight);
- }
- if (info != null) {
- mCandidatesPane.addView(info);
- info.measure(WRAP_CONTENT, WRAP_CONTENT);
- final int width = info.getMeasuredWidth();
- y = info.getMeasuredHeight();
- FrameLayoutCompatUtils.placeViewAt(info, infoX, 0, width, y);
- infoX += width * 2;
- }
- } else {
- paint.setTextScaleX(1.0f);
- final int textWidth = getTextWidth(styled, paint);
- int available = paneWidth - x - params.mPadding;
- if (textWidth >= available) {
- // Needs new row, centering previous row.
- centeringCandidates(centeringFrom, lastView, x, paneWidth);
- x = 0;
- y += mCandidateStripHeight;
- }
- if (x != 0) {
- // Add divider if this isn't the left most suggestion in current row.
- final View divider = mDividers.get(i);
- mCandidatesPane.addView(divider);
- FrameLayoutCompatUtils.placeViewAt(
- divider, x, y + (mCandidateStripHeight - params.mDividerHeight) / 2,
- params.mDividerWidth, params.mDividerHeight);
- x += params.mDividerWidth;
- }
- available = paneWidth - x - params.mPadding;
- text = getEllipsizedText(styled, available, paint);
- scaleX = paint.getTextScaleX();
- word.setText(text);
- word.setTextScaleX(scaleX);
- mCandidatesPane.addView(word);
- lastView = word;
- if (x == 0) centeringFrom = word;
- word.measure(WRAP_CONTENT,
- MeasureSpec.makeMeasureSpec(mCandidateStripHeight, MeasureSpec.EXACTLY));
- final int width = word.getMeasuredWidth();
- final int height = word.getMeasuredHeight();
- FrameLayoutCompatUtils.placeViewAt(
- word, x, y + (mCandidateStripHeight - height) / 2, width, height);
- x += width;
- if (info != null) {
- mCandidatesPane.addView(info);
- lastView = info;
- info.measure(WRAP_CONTENT, WRAP_CONTENT);
- final int infoWidth = info.getMeasuredWidth();
- FrameLayoutCompatUtils.placeViewAt(
- info, x - infoWidth, y, infoWidth, info.getMeasuredHeight());
+ private static CharSequence getDebugInfo(SuggestedWords suggestions, int pos) {
+ if (DBG && pos < suggestions.size()) {
+ final SuggestedWordInfo wordInfo = suggestions.getInfo(pos);
+ if (wordInfo != null) {
+ final CharSequence debugInfo = wordInfo.getDebugString();
+ if (!TextUtils.isEmpty(debugInfo)) {
+ return debugInfo;
}
}
}
- if (x != 0) {
- // Centering last candidates row.
- centeringCandidates(centeringFrom, lastView, x, paneWidth);
- }
+ return null;
}
private static void setLayoutWeight(View v, float weight, int height) {
@@ -585,13 +608,13 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
}
- private void centeringCandidates(View from, View to, int width, int paneWidth) {
- final ViewGroup pane = mCandidatesPane;
- final int fromIndex = pane.indexOfChild(from);
- final int toIndex = pane.indexOfChild(to);
- final int offset = (paneWidth - width) / 2;
+ private static void centeringCandidates(ViewGroup parent, View from, View to, int width,
+ int parentWidth) {
+ final int fromIndex = parent.indexOfChild(from);
+ final int toIndex = parent.indexOfChild(to);
+ final int offset = (parentWidth - width) / 2;
for (int index = fromIndex; index <= toIndex; index++) {
- offsetMargin(pane.getChildAt(index), offset, 0);
+ offsetMargin(parent.getChildAt(index), offset, 0);
}
}
@@ -609,14 +632,17 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
TextPaint paint) {
paint.setTextScaleX(1.0f);
final int width = getTextWidth(text, paint);
- final float scaleX = Math.min(maxWidth / (float)width, 1.0f);
+ if (width <= maxWidth) {
+ return text;
+ }
+ final float scaleX = maxWidth / (float)width;
if (scaleX >= MIN_TEXT_XSCALE) {
paint.setTextScaleX(scaleX);
return text;
}
// Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To get
- // squeezed and ellipsezed text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
+ // squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
final CharSequence ellipsized = TextUtils.ellipsize(
text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE);
paint.setTextScaleX(MIN_TEXT_XSCALE);
@@ -655,31 +681,30 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
private void expandCandidatesPane() {
- mExpandCandidatesPane.setVisibility(GONE);
- mCloseCandidatesPane.setVisibility(VISIBLE);
mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight());
mCandidatesPaneContainer.setVisibility(VISIBLE);
mKeyboardView.setVisibility(GONE);
}
private void closeCandidatesPane() {
- mExpandCandidatesPane.setVisibility(VISIBLE);
- mCloseCandidatesPane.setVisibility(GONE);
mCandidatesPaneContainer.setVisibility(GONE);
mKeyboardView.setVisibility(VISIBLE);
}
+ private void toggleCandidatesPane() {
+ if (mCandidatesPaneContainer.getVisibility() == VISIBLE) {
+ closeCandidatesPane();
+ } else {
+ expandCandidatesPane();
+ }
+ }
+
public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) {
- if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0)
+ final CharSequence inverted = mStripParams.getInvertedText(autoCorrectedWord);
+ if (inverted == null)
return;
final TextView tv = mWords.get(1);
- final Spannable word = new SpannableString(autoCorrectedWord);
- final int wordLength = word.length();
- word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength,
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- word.setSpan(mInvertedForegroundColorSpan, 0, wordLength,
- Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- tv.setText(word);
+ tv.setText(inverted);
mShowingAutoCorrectionInverted = true;
}
@@ -691,7 +716,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
mWordToSave.setText(word);
mShowingAddToDictionary = true;
mCandidatesStrip.setVisibility(GONE);
- mCandidatesPaneControl.setVisibility(GONE);
mTouchToSave.setVisibility(VISIBLE);
}
@@ -724,7 +748,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
return;
final TextView previewText = mPreviewText;
- previewText.setTextColor(mColorTypedWord);
+ previewText.setTextColor(mStripParams.mColorTypedWord);
previewText.setText(word);
previewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
@@ -751,18 +775,11 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
@Override
public boolean onLongClick(View view) {
- final Object tag = view.getTag();
- if (!(tag instanceof Integer))
+ if (mStripParams.mMoreSuggestionsAvailable) {
+ toggleCandidatesPane();
return true;
- final int index = (Integer) tag;
- if (index >= mSuggestions.size())
- return true;
-
- final CharSequence word = mSuggestions.getWord(index);
- if (word.length() < 2)
- return false;
- addToDictionary(word);
- return true;
+ }
+ return false;
}
@Override
@@ -773,6 +790,11 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
return;
}
+ if (view == mCandidatesPane) {
+ closeCandidatesPane();
+ return;
+ }
+
final Object tag = view.getTag();
if (!(tag instanceof Integer))
return;
diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionary.java b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
index 66a041508..8a7dfb839 100644
--- a/java/src/com/android/inputmethod/latin/ContactsDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
@@ -49,20 +49,28 @@ public class ContactsDictionary extends ExpandableDictionary {
private long mLastLoadedContacts;
- public ContactsDictionary(Context context, int dicTypeId) {
+ public ContactsDictionary(final Context context, final int dicTypeId) {
super(context, dicTypeId);
+ registerObserver(context);
+ loadDictionary();
+ }
+
+ private synchronized void registerObserver(final Context context) {
// Perform a managed query. The Activity will handle closing and requerying the cursor
// when needed.
+ if (mObserver != null) return;
ContentResolver cres = context.getContentResolver();
-
cres.registerContentObserver(
- Contacts.CONTENT_URI, true,mObserver = new ContentObserver(null) {
+ Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) {
@Override
public void onChange(boolean self) {
setRequiresReload(true);
}
});
- loadDictionary();
+ }
+
+ public void reopen(final Context context) {
+ registerObserver(context);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index fd62d61c3..2f1e7c2b8 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -33,7 +33,6 @@ public class DebugSettings extends PreferenceActivity
private boolean mServiceNeedsRestart = false;
private CheckBoxPreference mDebugMode;
- private CheckBoxPreference mUseSpacebarLanguageSwitch;
@Override
protected void onCreate(Bundle icicle) {
@@ -61,13 +60,6 @@ public class DebugSettings extends PreferenceActivity
updateDebugMode();
mServiceNeedsRestart = true;
}
- } else if (key.equals(SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCH_KEY)) {
- if (mUseSpacebarLanguageSwitch != null) {
- mUseSpacebarLanguageSwitch.setChecked(
- prefs.getBoolean(SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCH_KEY,
- getResources().getBoolean(
- R.bool.config_use_spacebar_language_switcher)));
- }
}
}
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index c7737b9a2..c35b42877 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -1,12 +1,12 @@
/*
* Copyright (C) 2008 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
@@ -16,6 +16,8 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.keyboard.ProximityInfo;
+
/**
* Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key
* strokes.
@@ -25,7 +27,7 @@ public abstract class Dictionary {
* Whether or not to replicate the typed word in the suggested list, even if it's valid.
*/
protected static final boolean INCLUDE_TYPED_WORD_IF_VALID = false;
-
+
/**
* The weight to give to a word if it's length is the same as the number of typed characters.
*/
@@ -57,13 +59,15 @@ public abstract class Dictionary {
}
/**
- * Searches for words in the dictionary that match the characters in the composer. Matched
+ * Searches for words in the dictionary that match the characters in the composer. Matched
* words are added through the callback object.
* @param composer the key sequence to match
* @param callback the callback object to send matched words to as possible candidates
+ * @param proximityInfo the object for key proximity. May be ignored by some implementations.
* @see WordCallback#addWord(char[], int, int, int, int, DataType)
*/
- abstract public void getWords(final WordComposer composer, final WordCallback callback);
+ abstract public void getWords(final WordComposer composer, final WordCallback callback,
+ final ProximityInfo proximityInfo);
/**
* Searches for pairs in the bigram dictionary that matches the previous word and all the
@@ -83,7 +87,7 @@ public abstract class Dictionary {
* @return true if the word exists, false otherwise
*/
abstract public boolean isValidWord(CharSequence word);
-
+
/**
* Compares the contents of the character array with the typed word and returns true if they
* are the same.
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 5e7de3e6b..739153044 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -16,7 +16,10 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.keyboard.ProximityInfo;
+
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -32,17 +35,24 @@ public class DictionaryCollection extends Dictionary {
}
public DictionaryCollection(Dictionary... dictionaries) {
- mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+ if (null == dictionaries) {
+ mDictionaries = new CopyOnWriteArrayList<Dictionary>();
+ } else {
+ mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+ mDictionaries.removeAll(Collections.singleton(null));
+ }
}
public DictionaryCollection(Collection<Dictionary> dictionaries) {
mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+ mDictionaries.removeAll(Collections.singleton(null));
}
@Override
- public void getWords(final WordComposer composer, final WordCallback callback) {
+ public void getWords(final WordComposer composer, final WordCallback callback,
+ final ProximityInfo proximityInfo) {
for (final Dictionary dict : mDictionaries)
- dict.getWords(composer, callback);
+ dict.getWords(composer, callback, proximityInfo);
}
@Override
@@ -66,6 +76,6 @@ public class DictionaryCollection extends Dictionary {
}
public void addDictionary(Dictionary newDict) {
- mDictionaries.add(newDict);
+ if (null != newDict) mDictionaries.add(newDict);
}
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index bba331868..39b4f63a5 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -48,29 +48,61 @@ public class DictionaryFactory {
int fallbackResId) {
if (null == locale) {
Log.e(TAG, "No locale defined for dictionary");
- return new DictionaryCollection(createBinaryDictionary(context, fallbackResId));
+ return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale));
}
final List<Dictionary> dictList = new LinkedList<Dictionary>();
- for (final AssetFileAddress f : BinaryDictionaryGetter.getDictionaryFiles(locale,
- context, fallbackResId)) {
- dictList.add(new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, null));
+ final List<AssetFileAddress> assetFileList =
+ BinaryDictionaryGetter.getDictionaryFiles(locale, context, fallbackResId);
+ if (null != assetFileList) {
+ for (final AssetFileAddress f : assetFileList) {
+ final BinaryDictionary binaryDictionary =
+ new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, null);
+ if (binaryDictionary.isValidDictionary()) {
+ dictList.add(binaryDictionary);
+ }
+ }
}
- if (null == dictList) return null;
- return new DictionaryCollection(dictList);
+ // null == dictList is not supposed to be possible, but better safe than sorry and it's
+ // safer for future extension. In this case, rather than returning null, it should be safer
+ // to return an empty DictionaryCollection.
+ if (null == dictList) {
+ return new DictionaryCollection();
+ } else {
+ if (dictList.isEmpty()) {
+ // The list may be empty if no dictionaries have been added. The getter should not
+ // return an empty list, but if it does we end up here. Likewise, if the files
+ // we found could not be opened by the native code for any reason (format mismatch,
+ // file too big to fit in memory, etc) then we could have an empty list. In this
+ // case we want to fall back on the resource.
+ return new DictionaryCollection(createBinaryDictionary(context, fallbackResId,
+ locale));
+ } else {
+ return new DictionaryCollection(dictList);
+ }
+ }
}
/**
* Initializes a dictionary from a raw resource file
* @param context application context for reading resources
* @param resId the resource containing the raw binary dictionary
+ * @param locale the locale to use for the resource
* @return an initialized instance of BinaryDictionary
*/
- protected static BinaryDictionary createBinaryDictionary(Context context, int resId) {
+ protected static BinaryDictionary createBinaryDictionary(final Context context,
+ final int resId, final Locale locale) {
AssetFileDescriptor afd = null;
try {
- afd = context.getResources().openRawResourceFd(resId);
+ final Resources res = context.getResources();
+ if (null != locale) {
+ final Locale savedLocale = Utils.setSystemLocale(res, locale);
+ afd = res.openRawResourceFd(resId);
+ Utils.setSystemLocale(res, savedLocale);
+ } else {
+ afd = res.openRawResourceFd(resId);
+ }
if (afd == null) {
Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
return null;
diff --git a/java/src/com/android/inputmethod/latin/EditingUtils.java b/java/src/com/android/inputmethod/latin/EditingUtils.java
index e56aa695d..634dbbdfc 100644
--- a/java/src/com/android/inputmethod/latin/EditingUtils.java
+++ b/java/src/com/android/inputmethod/latin/EditingUtils.java
@@ -33,6 +33,7 @@ public class EditingUtils {
* Number of characters we want to look back in order to identify the previous word
*/
private static final int LOOKBACK_CHARACTER_NUM = 15;
+ private static final int INVALID_CURSOR_POSITION = -1;
private EditingUtils() {
// Unintentional empty constructor for singleton.
@@ -63,10 +64,11 @@ public class EditingUtils {
}
private static int getCursorPosition(InputConnection connection) {
+ if (null == connection) return INVALID_CURSOR_POSITION;
ExtractedText extracted = connection.getExtractedText(
new ExtractedTextRequest(), 0);
if (extracted == null) {
- return -1;
+ return INVALID_CURSOR_POSITION;
}
return extracted.startOffset + extracted.selectionStart;
}
@@ -79,6 +81,7 @@ public class EditingUtils {
* represents the cursor, then "hello " will be returned.
*/
public static String getWordAtCursor(InputConnection connection, String separators) {
+ // getWordRangeAtCursor returns null if the connection is null
Range r = getWordRangeAtCursor(connection, separators);
return (r == null) ? null : r.mWord;
}
@@ -88,6 +91,7 @@ public class EditingUtils {
* getWordAtCursor.
*/
public static void deleteWordAtCursor(InputConnection connection, String separators) {
+ // getWordRangeAtCursor returns null if the connection is null
Range range = getWordRangeAtCursor(connection, separators);
if (range == null) return;
@@ -165,6 +169,7 @@ public class EditingUtils {
public static CharSequence getPreviousWord(InputConnection connection,
String sentenceSeperators) {
//TODO: Should fix this. This could be slow!
+ if (null == connection) return null;
CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
return getPreviousWord(prev, sentenceSeperators);
}
@@ -194,6 +199,7 @@ public class EditingUtils {
}
public static CharSequence getThisWord(InputConnection connection, String sentenceSeperators) {
+ if (null == connection) return null;
final CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
return getThisWord(prev, sentenceSeperators);
}
@@ -256,12 +262,14 @@ public class EditingUtils {
int selStart, int selEnd, String wordSeparators) {
if (selStart == selEnd) {
// There is just a cursor, so get the word at the cursor
+ // getWordRangeAtCursor returns null if the connection is null
EditingUtils.Range range = getWordRangeAtCursor(ic, wordSeparators);
if (range != null && !TextUtils.isEmpty(range.mWord)) {
return new SelectedWord(selStart - range.mCharsBefore, selEnd + range.mCharsAfter,
range.mWord);
}
} else {
+ if (null == ic) return null;
// Is the previous character empty or a word separator? If not, return null.
CharSequence charsBefore = ic.getTextBeforeCursor(1, 0);
if (!isWordBoundary(charsBefore, wordSeparators)) {
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 97a4a1816..35d1541ff 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.os.AsyncTask;
import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.ProximityInfo;
import java.util.LinkedList;
@@ -193,7 +194,8 @@ public class ExpandableDictionary extends Dictionary {
}
@Override
- public void getWords(final WordComposer codes, final WordCallback callback) {
+ public void getWords(final WordComposer codes, final WordCallback callback,
+ final ProximityInfo proximityInfo) {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) startDictionaryLoadingTaskLocked();
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index b7a795221..c28e40d95 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -29,7 +29,6 @@ import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.os.Debug;
-import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
import android.preference.PreferenceActivity;
@@ -45,8 +44,6 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
-import android.view.Window;
-import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
@@ -63,9 +60,11 @@ import com.android.inputmethod.compat.SuggestionSpanUtils;
import com.android.inputmethod.deprecated.LanguageSwitcherProxy;
import com.android.inputmethod.deprecated.VoiceProxy;
import com.android.inputmethod.deprecated.recorrection.Recorrection;
+import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.KeyboardSwitcher.KeyboardLayoutState;
import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.LatinKeyboard;
import com.android.inputmethod.keyboard.LatinKeyboardView;
@@ -114,6 +113,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// Key events coming any faster than this are long-presses.
private static final int QUICK_PRESS = 200;
+ private static final int SCREEN_ORIENTATION_CHANGE_DETECTION_DELAY = 2;
+ private static final int ACCUMULATE_START_INPUT_VIEW_DELAY = 20;
+ private static final int RESTORE_KEYBOARD_STATE_DELAY = 500;
+
/**
* The name of the scheme used by the Package Manager to warn of a new package installation,
* replacement or removal.
@@ -142,8 +145,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private Suggest mSuggest;
private CompletionInfo[] mApplicationSpecifiedCompletions;
- private AlertDialog mOptionsDialog;
-
private InputMethodManagerCompatWrapper mImm;
private Resources mResources;
private SharedPreferences mPrefs;
@@ -156,6 +157,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private UserDictionary mUserDictionary;
private UserBigramDictionary mUserBigramDictionary;
private UserUnigramDictionary mUserUnigramDictionary;
+ private boolean mIsUserDictionaryAvaliable;
// TODO: Create an inner class to group options and pseudo-options to improve readability.
// These variables are initialized according to the {@link EditorInfo#inputType}.
@@ -168,7 +170,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private WordComposer mWordComposer = new WordComposer();
private CharSequence mBestWord;
private boolean mHasUncommittedTypedChars;
- private boolean mHasDictionary;
// Magic space: a space that should disappear on space/apostrophe insertion, move after the
// punctuation on punctuation insertion, and become a real space on alpha char insertion.
private boolean mJustAddedMagicSpace; // This indicates whether the last char is a magic space.
@@ -178,7 +179,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private int mCorrectionMode;
private int mCommittedLength;
- private int mOrientation;
// Keep track of the last selection range to decide if we need to show word alternatives
private int mLastSelectionStart;
private int mLastSelectionEnd;
@@ -197,6 +197,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// TODO: Move this flag to VoiceProxy
private boolean mConfigurationChanging;
+ // Member variables for remembering the current device orientation.
+ private int mDisplayOrientation;
+ private int mDisplayWidth;
+ private int mDisplayHeight;
+
// Object for reacting to adding/removing a dictionary pack.
private BroadcastReceiver mDictionaryPackInstallReceiver =
new DictionaryPackInstallBroadcastReceiver(this);
@@ -204,7 +209,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// Keeps track of most recently inserted text (multi-character key) for reverting
private CharSequence mEnteredText;
-
public final UIHandler mHandler = new UIHandler(this);
public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
@@ -216,6 +220,30 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 5;
private static final int MSG_SPACE_TYPED = 6;
private static final int MSG_SET_BIGRAM_PREDICTIONS = 7;
+ private static final int MSG_CONFIRM_ORIENTATION_CHANGE = 8;
+ private static final int MSG_START_INPUT_VIEW = 9;
+ private static final int MSG_RESTORE_KEYBOARD_LAYOUT = 10;
+
+ private static class OrientationChangeArgs {
+ public final int mOldWidth;
+ public final int mOldHeight;
+ private int mRetryCount;
+
+ public OrientationChangeArgs(int oldw, int oldh) {
+ mOldWidth = oldw;
+ mOldHeight = oldh;
+ mRetryCount = 0;
+ }
+
+ public boolean hasTimedOut() {
+ mRetryCount++;
+ return mRetryCount >= 10;
+ }
+
+ public boolean hasOrientationChangeFinished(DisplayMetrics dm) {
+ return dm.widthPixels != mOldWidth && dm.heightPixels != mOldHeight;
+ }
+ }
public UIHandler(LatinIME outerInstance) {
super(outerInstance);
@@ -264,6 +292,25 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
(LatinKeyboard)msg.obj);
}
break;
+ case MSG_CONFIRM_ORIENTATION_CHANGE: {
+ final OrientationChangeArgs args = (OrientationChangeArgs)msg.obj;
+ final Resources res = latinIme.mResources;
+ final DisplayMetrics dm = res.getDisplayMetrics();
+ if (args.hasTimedOut() || args.hasOrientationChangeFinished(dm)) {
+ latinIme.setDisplayGeometry(res.getConfiguration(), dm);
+ } else {
+ // It seems orientation changing is on going.
+ postConfirmOrientationChange(args);
+ }
+ break;
+ }
+ case MSG_START_INPUT_VIEW:
+ latinIme.onStartInputView((EditorInfo)msg.obj, false);
+ break;
+ case MSG_RESTORE_KEYBOARD_LAYOUT:
+ removeMessages(MSG_UPDATE_SHIFT_STATE);
+ ((KeyboardLayoutState)msg.obj).restore();
+ break;
}
}
@@ -353,6 +400,49 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
public boolean isAcceptingDoubleSpaces() {
return hasMessages(MSG_SPACE_TYPED);
}
+
+ public void postRestoreKeyboardLayout() {
+ final LatinIME latinIme = getOuterInstance();
+ final KeyboardLayoutState state = latinIme.mKeyboardSwitcher.getKeyboardState();
+ if (state.isValid()) {
+ removeMessages(MSG_RESTORE_KEYBOARD_LAYOUT);
+ sendMessageDelayed(
+ obtainMessage(MSG_RESTORE_KEYBOARD_LAYOUT, state),
+ RESTORE_KEYBOARD_STATE_DELAY);
+ }
+ }
+
+ private void postConfirmOrientationChange(OrientationChangeArgs args) {
+ removeMessages(MSG_CONFIRM_ORIENTATION_CHANGE);
+ // Will confirm whether orientation change has finished or not again.
+ sendMessageDelayed(obtainMessage(MSG_CONFIRM_ORIENTATION_CHANGE, args),
+ SCREEN_ORIENTATION_CHANGE_DETECTION_DELAY);
+ }
+
+ public void startOrientationChanging(int oldw, int oldh) {
+ postConfirmOrientationChange(new OrientationChangeArgs(oldw, oldh));
+ final LatinIME latinIme = getOuterInstance();
+ latinIme.mKeyboardSwitcher.getKeyboardState().save();
+ postRestoreKeyboardLayout();
+ }
+
+ public boolean postStartInputView(EditorInfo attribute) {
+ if (hasMessages(MSG_CONFIRM_ORIENTATION_CHANGE) || hasMessages(MSG_START_INPUT_VIEW)) {
+ removeMessages(MSG_START_INPUT_VIEW);
+ // Postpone onStartInputView by ACCUMULATE_START_INPUT_VIEW_DELAY and see if
+ // orientation change has finished.
+ sendMessageDelayed(obtainMessage(MSG_START_INPUT_VIEW, attribute),
+ ACCUMULATE_START_INPUT_VIEW_DELAY);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private void setDisplayGeometry(Configuration conf, DisplayMetrics metric) {
+ mDisplayOrientation = conf.orientation;
+ mDisplayWidth = metric.widthPixels;
+ mDisplayHeight = metric.heightPixels;
}
@Override
@@ -361,14 +451,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mPrefs = prefs;
LatinImeLogger.init(this, prefs);
LanguageSwitcherProxy.init(this, prefs);
- SubtypeSwitcher.init(this, prefs);
+ InputMethodManagerCompatWrapper.init(this);
+ SubtypeSwitcher.init(this);
KeyboardSwitcher.init(this, prefs);
Recorrection.init(this, prefs);
AccessibilityUtils.init(this, prefs);
super.onCreate();
- mImm = InputMethodManagerCompatWrapper.getInstance(this);
+ mImm = InputMethodManagerCompatWrapper.getInstance();
mInputMethodId = Utils.getInputMethodId(mImm, getPackageName());
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
@@ -391,7 +482,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
- mOrientation = res.getConfiguration().orientation;
+ setDisplayGeometry(res.getConfiguration(), res.getDisplayMetrics());
// Register to receive ringer mode change and network state change.
// Also receive installation and removal of a dictionary pack.
@@ -418,7 +509,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
if (null == mSubtypeSwitcher) mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mSettingsValues = new Settings.Values(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr());
- resetContactsDictionary();
+ resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
}
private void initSuggest() {
@@ -427,8 +518,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
final Resources res = mResources;
final Locale savedLocale = Utils.setSystemLocale(res, keyboardLocale);
+ final ContactsDictionary oldContactsDictionary;
if (mSuggest != null) {
+ oldContactsDictionary = mSuggest.getContactsDictionary();
mSuggest.close();
+ } else {
+ oldContactsDictionary = null;
}
int mainDicResId = Utils.getMainDictionaryResourceId(res);
@@ -440,8 +535,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mUserDictionary = new UserDictionary(this, localeStr);
mSuggest.setUserDictionary(mUserDictionary);
+ mIsUserDictionaryAvaliable = mUserDictionary.isEnabled();
- resetContactsDictionary();
+ resetContactsDictionary(oldContactsDictionary);
mUserUnigramDictionary
= new UserUnigramDictionary(this, this, localeStr, Suggest.DIC_USER_UNIGRAM);
@@ -456,11 +552,36 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
Utils.setSystemLocale(res, savedLocale);
}
- private void resetContactsDictionary() {
- if (null == mSuggest) return;
- ContactsDictionary contactsDictionary = mSettingsValues.mUseContactsDict
- ? new ContactsDictionary(this, Suggest.DIC_CONTACTS) : null;
- mSuggest.setContactsDictionary(contactsDictionary);
+ /**
+ * Resets the contacts dictionary in mSuggest according to the user settings.
+ *
+ * This method takes an optional contacts dictionary to use. Since the contacts dictionary
+ * does not depend on the locale, it can be reused across different instances of Suggest.
+ * The dictionary will also be opened or closed as necessary depending on the settings.
+ *
+ * @param oldContactsDictionary an optional dictionary to use, or null
+ */
+ private void resetContactsDictionary(final ContactsDictionary oldContactsDictionary) {
+ final boolean shouldSetDictionary = (null != mSuggest && mSettingsValues.mUseContactsDict);
+
+ final ContactsDictionary dictionaryToUse;
+ if (!shouldSetDictionary) {
+ // Make sure the dictionary is closed. If it is already closed, this is a no-op,
+ // so it's safe to call it anyways.
+ if (null != oldContactsDictionary) oldContactsDictionary.close();
+ dictionaryToUse = null;
+ } else if (null != oldContactsDictionary) {
+ // Make sure the old contacts dictionary is opened. If it is already open, this is a
+ // no-op, so it's safe to call it anyways.
+ oldContactsDictionary.reopen(this);
+ dictionaryToUse = oldContactsDictionary;
+ } else {
+ dictionaryToUse = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
+ }
+
+ if (null != mSuggest) {
+ mSuggest.setContactsDictionary(dictionaryToUse);
+ }
}
/* package private */ void resetSuggestMainDict() {
@@ -488,11 +609,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
public void onConfigurationChanged(Configuration conf) {
mSubtypeSwitcher.onConfigurationChanged(conf);
// If orientation changed while predicting, commit the change
- if (conf.orientation != mOrientation) {
- InputConnection ic = getCurrentInputConnection();
+ if (conf.orientation != mDisplayOrientation) {
+ mHandler.startOrientationChanging(mDisplayWidth, mDisplayHeight);
+ final InputConnection ic = getCurrentInputConnection();
commitTyped(ic);
if (ic != null) ic.finishComposingText(); // For voice input
- mOrientation = conf.orientation;
if (isShowingOptionDialog())
mOptionsDialog.dismiss();
}
@@ -529,6 +650,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
@Override
public void onStartInputView(EditorInfo attribute, boolean restarting) {
+ mHandler.postRestoreKeyboardLayout();
+ if (mHandler.postStartInputView(attribute)) {
+ return;
+ }
+
final KeyboardSwitcher switcher = mKeyboardSwitcher;
LatinKeyboardView inputView = switcher.getKeyboardView();
@@ -550,8 +676,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// know now whether this is a password text field, because we need to know now whether we
// want to enable the voice button.
final VoiceProxy voiceIme = mVoiceProxy;
- voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(attribute.inputType)
- || InputTypeCompatUtils.isVisiblePasswordInputType(attribute.inputType));
+ final int inputType = (attribute != null) ? attribute.inputType : 0;
+ voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(inputType)
+ || InputTypeCompatUtils.isVisiblePasswordInputType(inputType));
initializeInputAttributes(attribute);
@@ -576,10 +703,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
LanguageSwitcherProxy.loadSettings();
if (mSubtypeSwitcher.isKeyboardMode()) {
- switcher.loadKeyboard(attribute,
- mSubtypeSwitcher.isShortcutImeEnabled() && voiceIme.isVoiceButtonEnabled(),
- voiceIme.isVoiceButtonOnPrimary());
- switcher.updateShiftState();
+ switcher.loadKeyboard(attribute, mSettingsValues);
}
if (mCandidateView != null)
@@ -588,8 +712,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// Delay updating suggestions because keyboard input view may not be shown at this point.
mHandler.postUpdateSuggestions();
- updateCorrectionMode();
-
inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
mSettingsValues.mKeyPreviewPopupDismissDelay);
inputView.setProximityCorrectionEnabled(true);
@@ -668,7 +790,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
super.onFinishInput();
LatinImeLogger.commit();
- mKeyboardSwitcher.onAutoCorrectionStateChanged(false);
mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging);
@@ -681,6 +802,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
@Override
public void onFinishInputView(boolean finishingInput) {
super.onFinishInputView(finishingInput);
+ mKeyboardSwitcher.onFinishInputView();
KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
if (inputView != null) inputView.cancelAllMessages();
// Remove pending messages related to update suggestions
@@ -719,7 +841,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
final boolean selectionChanged = (newSelStart != candidatesEnd
|| newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart;
final boolean candidatesCleared = candidatesStart == -1 && candidatesEnd == -1;
- if (((mComposingStringBuilder.length() > 0 && mHasUncommittedTypedChars)
+ if (!mExpectingUpdateSelection
+ && ((mComposingStringBuilder.length() > 0 && mHasUncommittedTypedChars)
|| mVoiceProxy.isVoiceInputHighlighted())
&& (selectionChanged || candidatesCleared)) {
if (candidatesCleared) {
@@ -737,7 +860,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
setPunctuationSuggestions();
}
TextEntryState.reset();
- InputConnection ic = getCurrentInputConnection();
+ final InputConnection ic = getCurrentInputConnection();
if (ic != null) {
ic.finishComposingText();
}
@@ -802,7 +925,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
@Override
public void hideWindow() {
LatinImeLogger.commit();
- mKeyboardSwitcher.onAutoCorrectionStateChanged(false);
+ mKeyboardSwitcher.onHideWindow();
if (TRACE) Debug.stopMethodTracing();
if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
@@ -847,7 +970,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
if (onEvaluateInputViewShown() && mCandidateViewContainer != null) {
final boolean shouldShowCandidates = shown
&& (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true);
- if (isExtractViewShown()) {
+ if (isFullscreenMode()) {
// No need to have extra space to show the key preview.
mCandidateViewContainer.setMinimumHeight(0);
mCandidateViewContainer.setVisibility(
@@ -942,7 +1065,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
event.getAction(), event.getKeyCode(), event.getRepeatCount(),
event.getDeviceId(), event.getScanCode(),
KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
- InputConnection ic = getCurrentInputConnection();
+ final InputConnection ic = getCurrentInputConnection();
if (ic != null)
ic.sendKeyEvent(newEvent);
return true;
@@ -952,12 +1075,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
return super.onKeyUp(keyCode, event);
}
- public void commitTyped(InputConnection inputConnection) {
+ public void commitTyped(final InputConnection ic) {
if (!mHasUncommittedTypedChars) return;
mHasUncommittedTypedChars = false;
if (mComposingStringBuilder.length() > 0) {
- if (inputConnection != null) {
- inputConnection.commitText(mComposingStringBuilder, 1);
+ if (ic != null) {
+ ic.commitText(mComposingStringBuilder, 1);
}
mCommittedLength = mComposingStringBuilder.length();
TextEntryState.acceptedTyped(mComposingStringBuilder);
@@ -968,7 +1091,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
public boolean getCurrentAutoCapsState() {
- InputConnection ic = getCurrentInputConnection();
+ final InputConnection ic = getCurrentInputConnection();
EditorInfo ei = getCurrentInputEditorInfo();
if (mSettingsValues.mAutoCap && ic != null && ei != null
&& ei.inputType != InputType.TYPE_NULL) {
@@ -992,25 +1115,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
- private static boolean canBeFollowedByPeriod(final int codePoint) {
- // TODO: Check again whether there really ain't a better way to check this.
- // TODO: This should probably be language-dependant...
- return Character.isLetterOrDigit(codePoint)
- || codePoint == Keyboard.CODE_SINGLE_QUOTE
- || codePoint == Keyboard.CODE_DOUBLE_QUOTE
- || codePoint == Keyboard.CODE_CLOSING_PARENTHESIS
- || codePoint == Keyboard.CODE_CLOSING_SQUARE_BRACKET
- || codePoint == Keyboard.CODE_CLOSING_CURLY_BRACKET
- || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
- }
-
private void maybeDoubleSpace() {
if (mCorrectionMode == Suggest.CORRECTION_NONE) return;
final InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
final CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
if (lastThree != null && lastThree.length() == 3
- && canBeFollowedByPeriod(lastThree.charAt(0))
+ && Utils.canBeFollowedByPeriod(lastThree.charAt(0))
&& lastThree.charAt(1) == Keyboard.CODE_SPACE
&& lastThree.charAt(2) == Keyboard.CODE_SPACE
&& mHandler.isAcceptingDoubleSpaces()) {
@@ -1026,10 +1137,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
- private void maybeRemovePreviousPeriod(CharSequence text) {
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
-
+ // "ic" must not null
+ private void maybeRemovePreviousPeriod(final InputConnection ic, CharSequence text) {
// When the text's first character is '.', remove the previous period
// if there is one.
CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
@@ -1069,25 +1178,31 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
private void onSettingsKeyPressed() {
- if (isShowingOptionDialog())
- return;
+ if (isShowingOptionDialog()) return;
if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
showSubtypeSelectorAndSettings();
- } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
+ } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm, false /* exclude aux subtypes */)) {
showOptionsMenu();
} else {
launchSettings();
}
}
- private void onSettingsKeyLongPressed() {
- if (!isShowingOptionDialog()) {
- if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) {
+ // Virtual codes representing custom requests. These are used in onCustomRequest() below.
+ public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1;
+
+ @Override
+ public boolean onCustomRequest(int requestCode) {
+ if (isShowingOptionDialog()) return false;
+ switch (requestCode) {
+ case CODE_SHOW_INPUT_METHOD_PICKER:
+ if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm, true /* include aux subtypes */)) {
mImm.showInputMethodPicker();
- } else {
- launchSettings();
+ return true;
}
+ return false;
}
+ return false;
}
private boolean isShowingOptionDialog() {
@@ -1131,15 +1246,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
case Keyboard.CODE_SETTINGS:
onSettingsKeyPressed();
break;
- case Keyboard.CODE_SETTINGS_LONGPRESS:
- onSettingsKeyLongPressed();
- break;
- case LatinKeyboard.CODE_NEXT_LANGUAGE:
- toggleLanguage(true);
- break;
- case LatinKeyboard.CODE_PREV_LANGUAGE:
- toggleLanguage(false);
- break;
case Keyboard.CODE_CAPSLOCK:
switcher.toggleCapsLock();
break;
@@ -1174,12 +1280,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
@Override
public void onTextInput(CharSequence text) {
mVoiceProxy.commitVoiceInput();
- InputConnection ic = getCurrentInputConnection();
+ final InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
mRecorrection.abortRecorrection(false);
ic.beginBatchEdit();
commitTyped(ic);
- maybeRemovePreviousPeriod(text);
+ maybeRemovePreviousPeriod(ic, text);
ic.commitText(text, 1);
ic.endBatchEdit();
mKeyboardSwitcher.updateShiftState();
@@ -1229,14 +1335,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
TextEntryState.backspace();
if (TextEntryState.isUndoCommit()) {
- revertLastWord(deleteChar);
+ revertLastWord(ic);
ic.endBatchEdit();
return;
}
if (justReplacedDoubleSpace) {
- if (revertDoubleSpace()) {
- ic.endBatchEdit();
- return;
+ if (revertDoubleSpace(ic)) {
+ ic.endBatchEdit();
+ return;
}
}
@@ -1251,7 +1357,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// different behavior only in the case of picking the first
// suggestion (typed word). It's intentional to have made this
// inconsistent with backspacing after selecting other suggestions.
- revertLastWord(true /* deleteChar */);
+ revertLastWord(ic);
} else {
sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
if (mDeleteCount > DELETE_ACCELERATE_AT) {
@@ -1297,7 +1403,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
int code = primaryCode;
- if (isAlphabet(code) && isSuggestionsRequested() && !isCursorTouchingWord()) {
+ if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code))
+ && isSuggestionsRequested() && !isCursorTouchingWord()) {
if (!mHasUncommittedTypedChars) {
mHasUncommittedTypedChars = true;
mComposingStringBuilder.setLength(0);
@@ -1334,7 +1441,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
mComposingStringBuilder.append((char) code);
mWordComposer.add(code, keyCodes, x, y);
- InputConnection ic = getCurrentInputConnection();
+ final InputConnection ic = getCurrentInputConnection();
if (ic != null) {
// If it's the first letter, make note of auto-caps state
if (mWordComposer.size() == 1) {
@@ -1378,9 +1485,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// not to auto correct, but accept the typed word. For instance,
// in Italian dov' should not be expanded to dove' because the elision
// requires the last vowel to be removed.
- final boolean shouldAutoCorrect =
- (mSettingsValues.mAutoCorrectEnabled || mSettingsValues.mQuickFixes)
- && !mInputTypeNoAutoCorrect && mHasDictionary;
+ final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
+ && !mInputTypeNoAutoCorrect;
if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
pickedDefault = pickDefaultSuggestion(primaryCode);
} else {
@@ -1455,7 +1561,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
public boolean isShowingSuggestionsStrip() {
return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
|| (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
- && mOrientation == Configuration.ORIENTATION_PORTRAIT);
+ && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
}
public boolean isCandidateStripVisible() {
@@ -1514,10 +1620,17 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
final WordComposer wordComposer = mWordComposer;
// TODO: May need a better way of retrieving previous word
- CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(),
- mSettingsValues.mWordSeparators);
- SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
- mKeyboardSwitcher.getKeyboardView(), wordComposer, prevWord);
+ final InputConnection ic = getCurrentInputConnection();
+ final CharSequence prevWord;
+ if (null == ic) {
+ prevWord = null;
+ } else {
+ prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
+ }
+ // getSuggestedWordBuilder handles gracefully a null value of prevWord
+ final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
+ mKeyboardSwitcher.getKeyboardView(), wordComposer, prevWord,
+ mKeyboardSwitcher.getLatinKeyboard().getProximityInfo());
boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection();
final CharSequence typedWord = wordComposer.getTypedWord();
@@ -1594,15 +1707,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mSettingsValues.mWordSeparators);
final boolean recorrecting = TextEntryState.isRecorrecting();
- InputConnection ic = getCurrentInputConnection();
+ final InputConnection ic = getCurrentInputConnection();
if (ic != null) {
ic.beginBatchEdit();
}
if (mApplicationSpecifiedCompletionOn && mApplicationSpecifiedCompletions != null
&& index >= 0 && index < mApplicationSpecifiedCompletions.length) {
- CompletionInfo ci = mApplicationSpecifiedCompletions[index];
if (ic != null) {
- ic.commitCompletion(ci);
+ final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
+ ic.commitCompletion(completionInfo);
}
mCommittedLength = suggestion.length();
if (mCandidateView != null) {
@@ -1626,7 +1739,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// for punctuation entered through the suggestion strip, it should be considered
// a magic space even if it was a normal space. This is meant to help in case the user
// pressed space on purpose of displaying the suggestion strip punctuation.
- final char primaryCode = suggestion.charAt(0);
+ final int rawPrimaryCode = suggestion.charAt(0);
+ // Maybe apply the "bidi mirrored" conversions for parentheses
+ final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard();
+ final int primaryCode = keyboard.mIsRtlKeyboard
+ ? Key.getRtlParenthesisCode(rawPrimaryCode) : rawPrimaryCode;
+
final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : "";
final int toLeft = (ic == null || TextUtils.isEmpty(beforeText))
? 0 : beforeText.charAt(0);
@@ -1663,9 +1781,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
sendMagicSpace();
}
- // We should show the hint if the user pressed the first entry AND either:
+ // We should show the "Touch again to save" hint if the user pressed the first entry
+ // AND either:
// - There is no dictionary (we know that because we tried to load it => null != mSuggest
- // AND mHasDictionary is false)
+ // AND mSuggest.hasMainDictionary() is false)
// - There is a dictionary and the word is not in it
// Please note that if mSuggest is null, it means that everything is off: suggestion
// and correction, so we shouldn't try to show the hint
@@ -1673,7 +1792,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// to do with the autocorrection setting.
final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
// If there is no dictionary the hint should be shown.
- && (!mHasDictionary
+ && (!mSuggest.hasMainDictionary()
// If "suggestion" is not in the dictionary, the hint should be shown.
|| !AutoCorrection.isValidWord(
mSuggest.getUnigramDictionaries(), suggestion, true));
@@ -1694,7 +1813,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// take a noticeable delay to update them which may feel uneasy.
}
if (showingAddToDictionaryHint) {
- mCandidateView.showAddToDictionaryHint(suggestion);
+ if (mIsUserDictionaryAvaliable) {
+ mCandidateView.showAddToDictionaryHint(suggestion);
+ } else {
+ mHandler.postUpdateSuggestions();
+ }
}
if (ic != null) {
ic.endBatchEdit();
@@ -1705,10 +1828,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
* Commits the chosen word to the text field and saves it for later retrieval.
*/
private void commitBestWord(CharSequence bestWord) {
- KeyboardSwitcher switcher = mKeyboardSwitcher;
+ final KeyboardSwitcher switcher = mKeyboardSwitcher;
if (!switcher.isKeyboardAvailable())
return;
- InputConnection ic = getCurrentInputConnection();
+ final InputConnection ic = getCurrentInputConnection();
if (ic != null) {
mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators);
SuggestedWords suggestedWords = mCandidateView.getSuggestions();
@@ -1733,7 +1856,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(),
mSettingsValues.mWordSeparators);
SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
- mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord);
+ mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord,
+ mKeyboardSwitcher.getLatinKeyboard().getProximityInfo());
if (builder.size() > 0) {
// Explicitly supply an empty typed word (the no-second-arg version of
@@ -1788,16 +1912,19 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// We don't want to register as bigrams words separated by a separator.
// For example "I will, and you too" : we don't want the pair ("will" "and") to be
// a bigram.
- CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(),
- mSettingsValues.mWordSeparators);
- if (!TextUtils.isEmpty(prevWord)) {
- mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString());
+ final InputConnection ic = getCurrentInputConnection();
+ if (null != ic) {
+ final CharSequence prevWord =
+ EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
+ if (!TextUtils.isEmpty(prevWord)) {
+ mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString());
+ }
}
}
}
public boolean isCursorTouchingWord() {
- InputConnection ic = getCurrentInputConnection();
+ final InputConnection ic = getCurrentInputConnection();
if (ic == null) return false;
CharSequence toLeft = ic.getTextBeforeCursor(1, 0);
CharSequence toRight = ic.getTextAfterCursor(1, 0);
@@ -1814,36 +1941,34 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
return false;
}
- private boolean sameAsTextBeforeCursor(InputConnection ic, CharSequence text) {
+ // "ic" must not null
+ private boolean sameAsTextBeforeCursor(final InputConnection ic, CharSequence text) {
CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0);
return TextUtils.equals(text, beforeText);
}
- private void revertLastWord(boolean deleteChar) {
+ // "ic" must not null
+ private void revertLastWord(final InputConnection ic) {
if (mHasUncommittedTypedChars || mComposingStringBuilder.length() <= 0) {
sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
return;
}
- final InputConnection ic = getCurrentInputConnection();
- final CharSequence punctuation = ic.getTextBeforeCursor(1, 0);
- if (deleteChar) ic.deleteSurroundingText(1, 0);
+ final CharSequence separator = ic.getTextBeforeCursor(1, 0);
+ ic.deleteSurroundingText(1, 0);
final CharSequence textToTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0);
- final int toDeleteLength = (!TextUtils.isEmpty(textToTheLeft)
- && mSettingsValues.isWordSeparator(textToTheLeft.charAt(0)))
- ? mCommittedLength - 1 : mCommittedLength;
- ic.deleteSurroundingText(toDeleteLength, 0);
-
- // Re-insert punctuation only when the deleted character was word separator and the
- // composing text wasn't equal to the auto-corrected text.
- if (deleteChar
- && !TextUtils.isEmpty(punctuation)
- && mSettingsValues.isWordSeparator(punctuation.charAt(0))
+ ic.deleteSurroundingText(mCommittedLength, 0);
+
+ // Re-insert "separator" only when the deleted character was word separator and the
+ // composing text wasn't equal to the auto-corrected text which can be found before
+ // the cursor.
+ if (!TextUtils.isEmpty(separator)
+ && mSettingsValues.isWordSeparator(separator.charAt(0))
&& !TextUtils.equals(mComposingStringBuilder, textToTheLeft)) {
ic.commitText(mComposingStringBuilder, 1);
TextEntryState.acceptedTyped(mComposingStringBuilder);
- ic.commitText(punctuation, 1);
- TextEntryState.typedCharacter(punctuation.charAt(0), true,
+ ic.commitText(separator, 1);
+ TextEntryState.typedCharacter(separator.charAt(0), true,
WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
// Clear composing text
mComposingStringBuilder.setLength(0);
@@ -1856,9 +1981,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mHandler.postUpdateSuggestions();
}
- private boolean revertDoubleSpace() {
+ // "ic" must not null
+ private boolean revertDoubleSpace(final InputConnection ic) {
mHandler.cancelDoubleSpacesTimer();
- final InputConnection ic = getCurrentInputConnection();
// Here we test whether we indeed have a period and a space before us. This should not
// be needed, but it's there just in case something went wrong.
final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
@@ -1894,32 +2019,19 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
setInputView(mKeyboardSwitcher.onCreateInputView());
}
// Reload keyboard because the current language has been changed.
- mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(),
- mSubtypeSwitcher.isShortcutImeEnabled() && mVoiceProxy.isVoiceButtonEnabled(),
- mVoiceProxy.isVoiceButtonOnPrimary());
+ mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues);
initSuggest();
loadSettings();
- mKeyboardSwitcher.updateShiftState();
- }
-
- // "reset" and "next" are used only for USE_SPACEBAR_LANGUAGE_SWITCHER.
- private void toggleLanguage(boolean next) {
- if (mSubtypeSwitcher.useSpacebarLanguageSwitcher()) {
- mSubtypeSwitcher.toggleLanguage(next);
- }
- // The following is necessary because on API levels < 10, we don't get notified when
- // subtype changes.
- if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED)
- onRefreshKeyboard();
}
@Override
public void onPress(int primaryCode, boolean withSliding) {
- if (mKeyboardSwitcher.isVibrateAndSoundFeedbackRequired()) {
+ final KeyboardSwitcher switcher = mKeyboardSwitcher;
+ switcher.registerWindowWidth();
+ if (switcher.isVibrateAndSoundFeedbackRequired()) {
vibrate();
playKeyClick(primaryCode);
}
- KeyboardSwitcher switcher = mKeyboardSwitcher;
final boolean distinctMultiTouch = switcher.hasDistinctMultitouch();
if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) {
switcher.onPressShift(withSliding);
@@ -2015,9 +2127,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private void updateCorrectionMode() {
// TODO: cleanup messy flags
- mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false;
- final boolean shouldAutoCorrect = (mSettingsValues.mAutoCorrectEnabled
- || mSettingsValues.mQuickFixes) && !mInputTypeNoAutoCorrect && mHasDictionary;
+ final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
+ && !mInputTypeNoAutoCorrect;
mCorrectionMode = (shouldAutoCorrect && mSettingsValues.mAutoCorrectEnabled)
? Suggest.CORRECTION_FULL
: (shouldAutoCorrect ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE);
@@ -2031,7 +2142,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private void updateAutoTextEnabled() {
if (mSuggest == null) return;
- mSuggest.setQuickFixesEnabled(mSettingsValues.mQuickFixes
+ // We want to use autotext if the settings are asking for auto corrections, and if
+ // the input language is the same as the system language (because autotext will only
+ // work in the system language so if we are entering text in a different language we
+ // do not want it on).
+ // We used to look at the "quick fixes" option instead of mAutoCorrectEnabled, but
+ // this option was redundant and confusing and therefore removed.
+ mSuggest.setQuickFixesEnabled(mSettingsValues.mAutoCorrectEnabled
&& SubtypeSwitcher.getInstance().isSystemLanguageSameAsInputLanguage());
}
@@ -2048,14 +2165,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
protected void launchSettings() {
- launchSettings(Settings.class);
+ launchSettingsClass(Settings.class);
}
public void launchDebugSettings() {
- launchSettings(DebugSettings.class);
+ launchSettingsClass(DebugSettings.class);
}
- protected void launchSettings(Class<? extends PreferenceActivity> settingsClass) {
+ protected void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
handleClose();
Intent intent = new Intent();
intent.setClass(LatinIME.this, settingsClass);
@@ -2088,7 +2205,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
};
- showOptionsMenuInternal(title, items, listener);
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setItems(items, listener)
+ .setTitle(title);
+ showOptionDialogInternal(builder.create());
}
private void showOptionsMenu() {
@@ -2111,28 +2231,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
};
- showOptionsMenuInternal(title, items, listener);
- }
-
- private void showOptionsMenuInternal(CharSequence title, CharSequence[] items,
- DialogInterface.OnClickListener listener) {
- final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
- if (windowToken == null) return;
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setCancelable(true);
- builder.setIcon(R.drawable.ic_dialog_keyboard);
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.setItems(items, listener);
- builder.setTitle(title);
- mOptionsDialog = builder.create();
- mOptionsDialog.setCanceledOnTouchOutside(true);
- Window window = mOptionsDialog.getWindow();
- WindowManager.LayoutParams lp = window.getAttributes();
- lp.token = windowToken;
- lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
- window.setAttributes(lp);
- window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
- mOptionsDialog.show();
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setItems(items, listener)
+ .setTitle(title);
+ showOptionDialogInternal(builder.create());
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java
deleted file mode 100644
index eb740e111..000000000
--- a/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.latin;
-
-import android.content.Context;
-
-import java.util.List;
-import java.util.Locale;
-
-class PrivateBinaryDictionaryGetter {
- private PrivateBinaryDictionaryGetter() {}
- public static List<AssetFileAddress> getDictionaryFiles(Locale locale, Context context) {
- return null;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 33e9bc35f..4c2627be3 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -16,13 +16,6 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.compat.CompatUtils;
-import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
-import com.android.inputmethod.deprecated.VoiceProxy;
-import com.android.inputmethod.compat.VibratorCompatWrapper;
-import com.android.inputmethodcommon.InputMethodSettingsActivity;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -40,12 +33,20 @@ import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
-import android.speech.SpeechRecognizer;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.util.Log;
+import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
+import com.android.inputmethod.compat.CompatUtils;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
+import com.android.inputmethod.compat.InputTypeCompatUtils;
+import com.android.inputmethod.compat.VibratorCompatWrapper;
+import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethodcommon.InputMethodSettingsActivity;
+
import java.util.Arrays;
import java.util.Locale;
@@ -60,7 +61,7 @@ public class Settings extends InputMethodSettingsActivity
public static final String PREF_KEY_PREVIEW_POPUP_ON = "popup_on";
public static final String PREF_RECORRECTION_ENABLED = "recorrection_enabled";
public static final String PREF_AUTO_CAP = "auto_cap";
- public static final String PREF_SETTINGS_KEY = "settings_key";
+ public static final String PREF_SHOW_SETTINGS_KEY = "show_settings_key";
public static final String PREF_VOICE_SETTINGS_KEY = "voice_mode";
public static final String PREF_INPUT_LANGUAGE = "input_language";
public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
@@ -68,7 +69,6 @@ public class Settings extends InputMethodSettingsActivity
public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key";
public static final String PREF_CORRECTION_SETTINGS_KEY = "correction_settings";
- public static final String PREF_QUICK_FIXES = "quick_fixes";
public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold";
public static final String PREF_DEBUG_SETTINGS = "debug_settings";
@@ -103,6 +103,7 @@ public class Settings extends InputMethodSettingsActivity
public final String mMagicSpaceSwappers;
public final String mSuggestPuncs;
public final SuggestedWords mSuggestPuncList;
+ private final String mSymbolsExcludedFromWordSeparators;
// From preferences:
public final boolean mSoundOn; // Sound setting private to Latin IME (see mSilentModeOn)
@@ -110,7 +111,6 @@ public class Settings extends InputMethodSettingsActivity
public final boolean mKeyPreviewPopupOn;
public final int mKeyPreviewPopupDismissDelay;
public final boolean mAutoCap;
- public final boolean mQuickFixes;
public final boolean mAutoCorrectEnabled;
public final double mAutoCorrectionThreshold;
// Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
@@ -119,6 +119,10 @@ public class Settings extends InputMethodSettingsActivity
public final boolean mBigramPredictionEnabled;
public final boolean mUseContactsDict;
+ private final boolean mShowSettingsKey;
+ private final boolean mVoiceKeyEnabled;
+ private final boolean mVoiceKeyOnMain;
+
public Values(final SharedPreferences prefs, final Context context,
final String localeStr) {
final Resources res = context.getResources();
@@ -149,10 +153,13 @@ public class Settings extends InputMethodSettingsActivity
mMagicSpaceSwappers = res.getString(R.string.magic_space_swapping_symbols);
String wordSeparators = mMagicSpaceStrippers + mMagicSpaceSwappers
+ res.getString(R.string.magic_space_promoting_symbols);
- final String notWordSeparators = res.getString(R.string.non_word_separator_symbols);
- for (int i = notWordSeparators.length() - 1; i >= 0; --i) {
- wordSeparators = wordSeparators.replace(notWordSeparators.substring(i, i + 1), "");
+ final String symbolsExcludedFromWordSeparators =
+ res.getString(R.string.symbols_excluded_from_word_separators);
+ for (int i = symbolsExcludedFromWordSeparators.length() - 1; i >= 0; --i) {
+ wordSeparators = wordSeparators.replace(
+ symbolsExcludedFromWordSeparators.substring(i, i + 1), "");
}
+ mSymbolsExcludedFromWordSeparators = symbolsExcludedFromWordSeparators;
mWordSeparators = wordSeparators;
mSuggestPuncs = res.getString(R.string.suggested_punctuations);
// TODO: it would be nice not to recreate this each time we change the configuration
@@ -163,21 +170,25 @@ public class Settings extends InputMethodSettingsActivity
mVibrateOn = hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON, false);
mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON,
res.getBoolean(R.bool.config_default_sound_enabled));
-
mKeyPreviewPopupOn = isKeyPreviewPopupEnabled(prefs, res);
mKeyPreviewPopupDismissDelay = getKeyPreviewPopupDismissDelay(prefs, res);
mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
- mQuickFixes = isQuickFixesEnabled(prefs, res);
-
mAutoCorrectEnabled = isAutoCorrectEnabled(prefs, res);
mBigramSuggestionEnabled = mAutoCorrectEnabled
&& isBigramSuggestionEnabled(prefs, res, mAutoCorrectEnabled);
mBigramPredictionEnabled = mBigramSuggestionEnabled
&& isBigramPredictionEnabled(prefs, res);
-
mAutoCorrectionThreshold = getAutoCorrectionThreshold(prefs, res);
-
mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
+ final boolean defaultShowSettingsKey = res.getBoolean(
+ R.bool.config_default_show_settings_key);
+ mShowSettingsKey = prefs.getBoolean(Settings.PREF_SHOW_SETTINGS_KEY,
+ defaultShowSettingsKey);
+ final String voiceModeMain = res.getString(R.string.voice_mode_main);
+ final String voiceModeOff = res.getString(R.string.voice_mode_off);
+ final String voiceMode = prefs.getString(PREF_VOICE_SETTINGS_KEY, voiceModeMain);
+ mVoiceKeyEnabled = voiceMode != null && !voiceMode.equals(voiceModeOff);
+ mVoiceKeyOnMain = voiceMode != null && voiceMode.equals(voiceModeMain);
Utils.setSystemLocale(res, savedLocale);
}
@@ -190,6 +201,10 @@ public class Settings extends InputMethodSettingsActivity
return mWordSeparators.contains(String.valueOf((char)code));
}
+ public boolean isSymbolExcludedFromWordSeparators(int code) {
+ return mSymbolsExcludedFromWordSeparators.contains(String.valueOf((char)code));
+ }
+
public boolean isMagicSpaceStripper(int code) {
return mMagicSpaceStrippers.contains(String.valueOf((char)code));
}
@@ -198,17 +213,6 @@ public class Settings extends InputMethodSettingsActivity
return mMagicSpaceSwappers.contains(String.valueOf((char)code));
}
- // Helper methods
- private static boolean isQuickFixesEnabled(SharedPreferences sp, Resources resources) {
- final boolean showQuickFixesOption = resources.getBoolean(
- R.bool.config_enable_quick_fixes_option);
- if (!showQuickFixesOption) {
- return isAutoCorrectEnabled(sp, resources);
- }
- return sp.getBoolean(Settings.PREF_QUICK_FIXES, resources.getBoolean(
- R.bool.config_default_quick_fixes));
- }
-
private static boolean isAutoCorrectEnabled(SharedPreferences sp, Resources resources) {
final String currentAutoCorrectionSetting = sp.getString(
Settings.PREF_AUTO_CORRECTION_THRESHOLD,
@@ -287,13 +291,28 @@ public class Settings extends InputMethodSettingsActivity
}
return builder.setIsPunctuationSuggestions().build();
}
+
+ public boolean isSettingsKeyEnabled(EditorInfo attribute) {
+ return mShowSettingsKey;
+ }
+
+ public boolean isVoiceKeyEnabled(EditorInfo attribute) {
+ final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
+ final int inputType = (attribute != null) ? attribute.inputType : 0;
+ return shortcutImeEnabled && mVoiceKeyEnabled
+ && !InputTypeCompatUtils.isPasswordInputType(inputType);
+ }
+
+ public boolean isVoiceKeyOnMain() {
+ return mVoiceKeyOnMain;
+ }
}
private PreferenceScreen mInputLanguageSelection;
private ListPreference mVoicePreference;
- private ListPreference mSettingsKeyPreference;
+ private CheckBoxPreference mShowSettingsKeyPreference;
private ListPreference mShowCorrectionSuggestionsPreference;
- private ListPreference mAutoCorrectionThreshold;
+ private ListPreference mAutoCorrectionThresholdPreference;
private ListPreference mKeyPreviewPopupDismissDelay;
// Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
private CheckBoxPreference mBigramSuggestion;
@@ -304,15 +323,13 @@ public class Settings extends InputMethodSettingsActivity
private AlertDialog mDialog;
- private VoiceProxy.VoiceLoggerWrapper mVoiceLogger;
-
private boolean mOkClicked = false;
private String mVoiceModeOff;
private void ensureConsistencyOfAutoCorrectionSettings() {
final String autoCorrectionOff = getResources().getString(
R.string.auto_correction_threshold_mode_index_off);
- final String currentSetting = mAutoCorrectionThreshold.getValue();
+ final String currentSetting = mAutoCorrectionThresholdPreference.getValue();
mBigramSuggestion.setEnabled(!currentSetting.equals(autoCorrectionOff));
mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff));
}
@@ -340,7 +357,7 @@ public class Settings extends InputMethodSettingsActivity
mInputLanguageSelection = (PreferenceScreen) findPreference(PREF_SUBTYPES);
mInputLanguageSelection.setOnPreferenceClickListener(this);
mVoicePreference = (ListPreference) findPreference(PREF_VOICE_SETTINGS_KEY);
- mSettingsKeyPreference = (ListPreference) findPreference(PREF_SETTINGS_KEY);
+ mShowSettingsKeyPreference = (CheckBoxPreference) findPreference(PREF_SHOW_SETTINGS_KEY);
mShowCorrectionSuggestionsPreference =
(ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING);
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
@@ -349,9 +366,9 @@ public class Settings extends InputMethodSettingsActivity
mVoiceModeOff = getString(R.string.voice_mode_off);
mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
.equals(mVoiceModeOff));
- mVoiceLogger = VoiceProxy.VoiceLoggerWrapper.getInstance(context);
- mAutoCorrectionThreshold = (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
+ mAutoCorrectionThresholdPreference =
+ (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTIONS);
mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS);
mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
@@ -372,7 +389,7 @@ public class Settings extends InputMethodSettingsActivity
final boolean showSettingsKeyOption = res.getBoolean(
R.bool.config_enable_show_settings_key_option);
if (!showSettingsKeyOption) {
- generalSettings.removePreference(mSettingsKeyPreference);
+ generalSettings.removePreference(mShowSettingsKeyPreference);
}
final boolean showVoiceKeyOption = res.getBoolean(
@@ -401,12 +418,6 @@ public class Settings extends InputMethodSettingsActivity
generalSettings.removePreference(findPreference(PREF_RECORRECTION_ENABLED));
}
- final boolean showQuickFixesOption = res.getBoolean(
- R.bool.config_enable_quick_fixes_option);
- if (!showQuickFixesOption) {
- textCorrectionGroup.removePreference(findPreference(PREF_QUICK_FIXES));
- }
-
final boolean showBigramSuggestionsOption = res.getBoolean(
R.bool.config_enable_bigram_suggestions_option);
if (!showBigramSuggestionsOption) {
@@ -447,16 +458,18 @@ public class Settings extends InputMethodSettingsActivity
}
}
+ @SuppressWarnings("unused")
@Override
public void onResume() {
super.onResume();
- if (!VoiceProxy.VOICE_INSTALLED
- || !SpeechRecognizer.isRecognitionAvailable(getActivityInternal())) {
- getPreferenceScreen().removePreference(mVoicePreference);
- } else {
+ final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
+ if (isShortcutImeEnabled
+ || (VoiceProxy.VOICE_INSTALLED
+ && VoiceProxy.isRecognitionAvailable(getActivityInternal()))) {
updateVoiceModeSummary();
+ } else {
+ getPreferenceScreen().removePreference(mVoicePreference);
}
- updateSettingsKeySummary();
updateShowCorrectionSuggestionsSummary();
updateKeyPreviewPopupDelaySummary();
}
@@ -488,7 +501,6 @@ public class Settings extends InputMethodSettingsActivity
mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
.equals(mVoiceModeOff));
updateVoiceModeSummary();
- updateSettingsKeySummary();
updateShowCorrectionSuggestionsSummary();
updateKeyPreviewPopupDelaySummary();
}
@@ -498,7 +510,7 @@ public class Settings extends InputMethodSettingsActivity
if (pref == mInputLanguageSelection) {
startActivity(CompatUtils.getInputLanguageSelectionIntent(
Utils.getInputMethodId(
- InputMethodManagerCompatWrapper.getInstance(getActivityInternal()),
+ InputMethodManagerCompatWrapper.getInstance(),
getActivityInternal().getApplicationInfo().packageName), 0));
return true;
}
@@ -512,12 +524,6 @@ public class Settings extends InputMethodSettingsActivity
mShowCorrectionSuggestionsPreference.getValue())]);
}
- private void updateSettingsKeySummary() {
- mSettingsKeyPreference.setSummary(
- getResources().getStringArray(R.array.settings_key_modes)
- [mSettingsKeyPreference.findIndexOfValue(mSettingsKeyPreference.getValue())]);
- }
-
private void updateKeyPreviewPopupDelaySummary() {
final ListPreference lp = mKeyPreviewPopupDismissDelay;
lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]);
@@ -541,6 +547,7 @@ public class Settings extends InputMethodSettingsActivity
[mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
}
+ @Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case VOICE_INPUT_CONFIRM_DIALOG:
@@ -549,12 +556,9 @@ public class Settings extends InputMethodSettingsActivity
public void onClick(DialogInterface dialog, int whichButton) {
if (whichButton == DialogInterface.BUTTON_NEGATIVE) {
mVoicePreference.setValue(mVoiceModeOff);
- mVoiceLogger.settingsWarningDialogCancel();
} else if (whichButton == DialogInterface.BUTTON_POSITIVE) {
mOkClicked = true;
- mVoiceLogger.settingsWarningDialogOk();
}
- updateVoicePreference();
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(getActivityInternal())
@@ -583,7 +587,6 @@ public class Settings extends InputMethodSettingsActivity
AlertDialog dialog = builder.create();
mDialog = dialog;
dialog.setOnDismissListener(this);
- mVoiceLogger.settingsWarningDialogShown();
return dialog;
default:
Log.e(TAG, "unknown dialog " + id);
@@ -593,16 +596,10 @@ public class Settings extends InputMethodSettingsActivity
@Override
public void onDismiss(DialogInterface dialog) {
- mVoiceLogger.settingsWarningDialogDismissed();
if (!mOkClicked) {
// This assumes that onPreferenceClick gets called first, and this if the user
// agreed after the warning, we set the mOkClicked value to true.
mVoicePreference.setValue(mVoiceModeOff);
}
}
-
- private void updateVoicePreference() {
- boolean isChecked = !mVoicePreference.getValue().equals(mVoiceModeOff);
- mVoiceLogger.voiceInputSettingEnabled(isChecked);
- }
}
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 8fc19ae87..0a391a77e 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -16,16 +16,8 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.compat.InputMethodInfoCompatWrapper;
-import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
-import com.android.inputmethod.deprecated.VoiceProxy;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.keyboard.LatinKeyboard;
-
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -37,6 +29,13 @@ import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
+import com.android.inputmethod.compat.InputMethodInfoCompatWrapper;
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
+import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.LatinKeyboard;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -52,7 +51,6 @@ public class SubtypeSwitcher {
private static final String VOICE_MODE = "voice";
private static final String SUBTYPE_EXTRAVALUE_REQUIRE_NETWORK_CONNECTIVITY =
"requireNetworkConnectivity";
- public static final String USE_SPACEBAR_LANGUAGE_SWITCH_KEY = "use_spacebar_language_switch";
private final TextUtils.SimpleStringSplitter mLocaleSplitter =
new TextUtils.SimpleStringSplitter(LOCALE_SEPARATER);
@@ -62,13 +60,10 @@ public class SubtypeSwitcher {
private /* final */ InputMethodManagerCompatWrapper mImm;
private /* final */ Resources mResources;
private /* final */ ConnectivityManager mConnectivityManager;
- private /* final */ boolean mConfigUseSpacebarLanguageSwitcher;
- private /* final */ SharedPreferences mPrefs;
private final ArrayList<InputMethodSubtypeCompatWrapper>
mEnabledKeyboardSubtypesOfCurrentInputMethod =
new ArrayList<InputMethodSubtypeCompatWrapper>();
private final ArrayList<String> mEnabledLanguagesOfCurrentInputMethod = new ArrayList<String>();
- private final LanguageBarInfo mLanguageBarInfo = new LanguageBarInfo();
/*-----------------------------------------------------------*/
// Variants which should be changed only by reload functions.
@@ -81,7 +76,6 @@ public class SubtypeSwitcher {
private Locale mSystemLocale;
private Locale mInputLocale;
private String mInputLocaleStr;
- private String mInputMethodId;
private VoiceProxy.VoiceInputWrapper mVoiceInputWrapper;
/*-----------------------------------------------------------*/
@@ -91,9 +85,9 @@ public class SubtypeSwitcher {
return sInstance;
}
- public static void init(LatinIME service, SharedPreferences prefs) {
+ public static void init(LatinIME service) {
SubtypeLocale.init(service);
- sInstance.initialize(service, prefs);
+ sInstance.initialize(service);
sInstance.updateAllParameters();
}
@@ -101,10 +95,10 @@ public class SubtypeSwitcher {
// Intentional empty constructor for singleton.
}
- private void initialize(LatinIME service, SharedPreferences prefs) {
+ private void initialize(LatinIME service) {
mService = service;
mResources = service.getResources();
- mImm = InputMethodManagerCompatWrapper.getInstance(service);
+ mImm = InputMethodManagerCompatWrapper.getInstance();
mConnectivityManager = (ConnectivityManager) service.getSystemService(
Context.CONNECTIVITY_SERVICE);
mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
@@ -115,11 +109,9 @@ public class SubtypeSwitcher {
mCurrentSubtype = null;
mAllEnabledSubtypesOfCurrentInputMethod = null;
mVoiceInputWrapper = null;
- mPrefs = prefs;
final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
mIsNetworkConnected = (info != null && info.isConnected());
- mInputMethodId = Utils.getInputMethodId(mImm, service.getPackageName());
}
// Update all parameters stored in SubtypeSwitcher.
@@ -133,9 +125,6 @@ public class SubtypeSwitcher {
// Update parameters which are changed outside LatinIME. This parameters affect UI so they
// should be updated every time onStartInputview.
public void updateParametersOnStartInputView() {
- mConfigUseSpacebarLanguageSwitcher = mPrefs.getBoolean(USE_SPACEBAR_LANGUAGE_SWITCH_KEY,
- mService.getResources().getBoolean(
- R.bool.config_use_spacebar_language_switcher));
updateEnabledSubtypes();
updateShortcutIME();
}
@@ -170,10 +159,6 @@ public class SubtypeSwitcher {
Log.w(TAG, "Last subtype was disabled. Update to the current one.");
}
updateSubtype(mImm.getCurrentInputMethodSubtype());
- } else {
- // mLanguageBarInfo.update() will be called in updateSubtype so there is no need
- // to call this in the if-clause above.
- mLanguageBarInfo.update();
}
}
@@ -273,7 +258,6 @@ public class SubtypeSwitcher {
mVoiceInputWrapper.reset();
}
}
- mLanguageBarInfo.update();
}
// Update the current input locale from Locale string.
@@ -334,7 +318,7 @@ public class SubtypeSwitcher {
// when the API level is 10 or previous.
mService.notifyOnCurrentInputMethodSubtypeChanged(subtype);
}
- }.execute();
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public Drawable getShortcutIcon() {
@@ -377,13 +361,17 @@ public class SubtypeSwitcher {
}
public boolean isShortcutImeEnabled() {
- if (mShortcutInputMethodInfo == null)
+ if (mShortcutInputMethodInfo == null) {
return false;
- if (mShortcutSubtype == null)
+ }
+ if (mShortcutSubtype == null) {
return true;
+ }
// For compatibility, if the shortcut subtype is dummy, we assume the shortcut IME
// (built-in voice dummy subtype) is available.
- if (!mShortcutSubtype.hasOriginalObject()) return true;
+ if (!mShortcutSubtype.hasOriginalObject()) {
+ return true;
+ }
final boolean allowsImplicitlySelectedSubtypes = true;
for (final InputMethodSubtypeCompatWrapper enabledSubtype :
mImm.getEnabledInputMethodSubtypeList(
@@ -427,10 +415,6 @@ public class SubtypeSwitcher {
return mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
}
- public boolean useSpacebarLanguageSwitcher() {
- return mConfigUseSpacebarLanguageSwitcher;
- }
-
public boolean needsToDisplayLanguage() {
return mNeedsToDisplayLanguage;
}
@@ -508,75 +492,6 @@ public class SubtypeSwitcher {
KeyboardSwitcher.getInstance().getKeyboardView().getWindowToken());
}
- //////////////////////////////////////
- // Spacebar Language Switch support //
- //////////////////////////////////////
-
- private class LanguageBarInfo {
- private int mCurrentKeyboardSubtypeIndex;
- private InputMethodSubtypeCompatWrapper mNextKeyboardSubtype;
- private InputMethodSubtypeCompatWrapper mPreviousKeyboardSubtype;
- private String mNextLanguage;
- private String mPreviousLanguage;
- public LanguageBarInfo() {
- update();
- }
-
- private String getNextLanguage() {
- return mNextLanguage;
- }
-
- private String getPreviousLanguage() {
- return mPreviousLanguage;
- }
-
- public InputMethodSubtypeCompatWrapper getNextKeyboardSubtype() {
- return mNextKeyboardSubtype;
- }
-
- public InputMethodSubtypeCompatWrapper getPreviousKeyboardSubtype() {
- return mPreviousKeyboardSubtype;
- }
-
- public void update() {
- if (!mConfigUseSpacebarLanguageSwitcher
- || mEnabledKeyboardSubtypesOfCurrentInputMethod == null
- || mEnabledKeyboardSubtypesOfCurrentInputMethod.size() == 0) return;
- mCurrentKeyboardSubtypeIndex = getCurrentIndex();
- mNextKeyboardSubtype = getNextKeyboardSubtypeInternal(mCurrentKeyboardSubtypeIndex);
- Locale locale = Utils.constructLocaleFromString(mNextKeyboardSubtype.getLocale());
- mNextLanguage = getFullDisplayName(locale, true);
- mPreviousKeyboardSubtype = getPreviousKeyboardSubtypeInternal(
- mCurrentKeyboardSubtypeIndex);
- locale = Utils.constructLocaleFromString(mPreviousKeyboardSubtype.getLocale());
- mPreviousLanguage = getFullDisplayName(locale, true);
- }
-
- private int normalize(int index) {
- final int N = mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
- final int ret = index % N;
- return ret < 0 ? ret + N : ret;
- }
-
- private int getCurrentIndex() {
- final int N = mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
- for (int i = 0; i < N; ++i) {
- if (mEnabledKeyboardSubtypesOfCurrentInputMethod.get(i).equals(mCurrentSubtype)) {
- return i;
- }
- }
- return 0;
- }
-
- private InputMethodSubtypeCompatWrapper getNextKeyboardSubtypeInternal(int index) {
- return mEnabledKeyboardSubtypesOfCurrentInputMethod.get(normalize(index + 1));
- }
-
- private InputMethodSubtypeCompatWrapper getPreviousKeyboardSubtypeInternal(int index) {
- return mEnabledKeyboardSubtypesOfCurrentInputMethod.get(normalize(index - 1));
- }
- }
-
public static String getFullDisplayName(Locale locale, boolean returnsNameInThisLocale) {
if (returnsNameInThisLocale) {
return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
@@ -609,14 +524,6 @@ public class SubtypeSwitcher {
return getDisplayLanguage(getInputLocale());
}
- public String getNextInputLanguageName() {
- return mLanguageBarInfo.getNextLanguage();
- }
-
- public String getPreviousInputLanguageName() {
- return mLanguageBarInfo.getPreviousLanguage();
- }
-
/////////////////////////////
// Other utility functions //
/////////////////////////////
@@ -653,24 +560,4 @@ public class SubtypeSwitcher {
supportedLocalesString.split("\\s+"));
return voiceInputSupportedLocales.contains(locale);
}
-
- private void changeToNextSubtype() {
- final InputMethodSubtypeCompatWrapper subtype =
- mLanguageBarInfo.getNextKeyboardSubtype();
- switchToTargetIME(mInputMethodId, subtype);
- }
-
- private void changeToPreviousSubtype() {
- final InputMethodSubtypeCompatWrapper subtype =
- mLanguageBarInfo.getPreviousKeyboardSubtype();
- switchToTargetIME(mInputMethodId, subtype);
- }
-
- public void toggleLanguage(boolean next) {
- if (next) {
- changeToNextSubtype();
- } else {
- changeToPreviousSubtype();
- }
- }
}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 8ae653f2f..a2d66f398 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -22,6 +22,8 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+import com.android.inputmethod.keyboard.ProximityInfo;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@@ -86,6 +88,7 @@ public class Suggest implements Dictionary.WordCallback {
private AutoCorrection mAutoCorrection;
private Dictionary mMainDict;
+ private ContactsDictionary mContactsDict;
private WhitelistDictionary mWhiteListDictionary;
private final Map<String, Dictionary> mUnigramDictionaries = new HashMap<String, Dictionary>();
private final Map<String, Dictionary> mBigramDictionaries = new HashMap<String, Dictionary>();
@@ -102,6 +105,8 @@ public class Suggest implements Dictionary.WordCallback {
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
+ // TODO: maybe this should be synchronized, it's quite scary as it is.
+ // TODO: if it becomes synchronized, also move initPool in the thread in initAsynchronously
private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
private CharSequence mTypedWord;
@@ -111,27 +116,39 @@ public class Suggest implements Dictionary.WordCallback {
private int mCorrectionMode = CORRECTION_BASIC;
- public Suggest(Context context, int dictionaryResId, Locale locale) {
- init(context, DictionaryFactory.createDictionaryFromManager(context, locale,
- dictionaryResId));
+ public Suggest(final Context context, final int dictionaryResId, final Locale locale) {
+ initAsynchronously(context, dictionaryResId, locale);
}
/* package for test */ Suggest(Context context, File dictionary, long startOffset, long length,
Flag[] flagArray) {
- init(null, DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset,
- length, flagArray));
+ initSynchronously(null, DictionaryFactory.createDictionaryForTest(context, dictionary,
+ startOffset, length, flagArray));
}
- private void init(Context context, Dictionary mainDict) {
- mMainDict = mainDict;
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
+ private void initWhitelistAndAutocorrectAndPool(final Context context) {
mWhiteListDictionary = WhitelistDictionary.init(context);
addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
mAutoCorrection = new AutoCorrection();
initPool();
}
+ private void initAsynchronously(final Context context, final int dictionaryResId,
+ final Locale locale) {
+ resetMainDict(context, dictionaryResId, locale);
+
+ // TODO: read the whitelist and init the pool asynchronously too.
+ // initPool should be done asynchronously but the pool is not thread-safe at the moment.
+ initWhitelistAndAutocorrectAndPool(context);
+ }
+
+ private void initSynchronously(Context context, Dictionary mainDict) {
+ mMainDict = mainDict;
+ addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
+ addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
+ initWhitelistAndAutocorrectAndPool(context);
+ }
+
private void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key,
Dictionary dict) {
final Dictionary oldDict = (dict == null)
@@ -142,12 +159,18 @@ public class Suggest implements Dictionary.WordCallback {
}
}
- public void resetMainDict(Context context, int dictionaryResId, Locale locale) {
- final Dictionary newMainDict = DictionaryFactory.createDictionaryFromManager(
- context, locale, dictionaryResId);
- mMainDict = newMainDict;
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict);
+ public void resetMainDict(final Context context, final int dictionaryResId,
+ final Locale locale) {
+ mMainDict = null;
+ new Thread("InitializeBinaryDictionary") {
+ public void run() {
+ final Dictionary newMainDict = DictionaryFactory.createDictionaryFromManager(
+ context, locale, dictionaryResId);
+ mMainDict = newMainDict;
+ addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict);
+ addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict);
+ }
+ }.start();
}
private void initPool() {
@@ -169,10 +192,16 @@ public class Suggest implements Dictionary.WordCallback {
mCorrectionMode = mode;
}
+ // The main dictionary could have been loaded asynchronously. Don't cache the return value
+ // of this method.
public boolean hasMainDictionary() {
return mMainDict != null;
}
+ public ContactsDictionary getContactsDictionary() {
+ return mContactsDict;
+ }
+
public Map<String, Dictionary> getUnigramDictionaries() {
return mUnigramDictionaries;
}
@@ -194,7 +223,8 @@ public class Suggest implements Dictionary.WordCallback {
* the contacts dictionary by passing null to this method. In this case no contacts dictionary
* won't be used.
*/
- public void setContactsDictionary(Dictionary contactsDictionary) {
+ public void setContactsDictionary(ContactsDictionary contactsDictionary) {
+ mContactsDict = contactsDictionary;
addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
}
@@ -243,9 +273,10 @@ public class Suggest implements Dictionary.WordCallback {
* @param prevWordForBigram previous word (used only for bigram)
* @return suggested words object.
*/
- public SuggestedWords getSuggestions(View view, WordComposer wordComposer,
- CharSequence prevWordForBigram) {
- return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram).build();
+ public SuggestedWords getSuggestions(final View view, final WordComposer wordComposer,
+ final CharSequence prevWordForBigram, final ProximityInfo proximityInfo) {
+ return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram,
+ proximityInfo).build();
}
private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) {
@@ -279,8 +310,9 @@ public class Suggest implements Dictionary.WordCallback {
}
// TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder
- public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer,
- CharSequence prevWordForBigram) {
+ public SuggestedWords.Builder getSuggestedWordBuilder(final View view,
+ final WordComposer wordComposer, CharSequence prevWordForBigram,
+ final ProximityInfo proximityInfo) {
LatinImeLogger.onStartSuggestion(prevWordForBigram);
mAutoCorrection.init();
mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
@@ -345,7 +377,7 @@ public class Suggest implements Dictionary.WordCallback {
if (key.equals(DICT_KEY_USER_UNIGRAM) || key.equals(DICT_KEY_WHITELIST))
continue;
final Dictionary dictionary = mUnigramDictionaries.get(key);
- dictionary.getWords(wordComposer, this);
+ dictionary.getWords(wordComposer, this, proximityInfo);
}
}
CharSequence autoText = null;
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 84db17504..c1c46fa47 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin;
+import android.text.TextUtils;
import android.view.inputmethod.CompletionInfo;
import java.util.ArrayList;
@@ -30,7 +31,7 @@ public class SuggestedWords {
public final boolean mTypedWordValid;
public final boolean mHasMinimalSuggestion;
public final boolean mIsPunctuationSuggestions;
- public final List<SuggestedWordInfo> mSuggestedWordInfoList;
+ private final List<SuggestedWordInfo> mSuggestedWordInfoList;
private SuggestedWords(List<CharSequence> words, boolean typedWordValid,
boolean hasMinimalSuggestion, boolean isPunctuationSuggestions,
@@ -54,6 +55,10 @@ public class SuggestedWords {
return mWords.get(pos);
}
+ public SuggestedWordInfo getInfo(int pos) {
+ return mSuggestedWordInfoList != null ? mSuggestedWordInfoList.get(pos) : null;
+ }
+
public boolean hasAutoCorrectionWord() {
return mHasMinimalSuggestion && size() > 1 && !mTypedWordValid;
}
@@ -105,14 +110,18 @@ public class SuggestedWords {
}
private Builder addWord(CharSequence word, SuggestedWordInfo suggestedWordInfo) {
- mWords.add(word);
- mSuggestedWordInfoList.add(suggestedWordInfo);
+ if (!TextUtils.isEmpty(word)) {
+ mWords.add(word);
+ // It's okay if suggestedWordInfo is null since it's checked where it's used.
+ mSuggestedWordInfoList.add(suggestedWordInfo);
+ }
return this;
}
public Builder setApplicationSpecifiedCompletions(CompletionInfo[] infos) {
- for (CompletionInfo info : infos)
- addWord(info.getText());
+ for (CompletionInfo info : infos) {
+ if (null != info) addWord(info.getText());
+ }
return this;
}
diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java
index 2aaa26c8d..6608d8268 100644
--- a/java/src/com/android/inputmethod/latin/UserDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserDictionary.java
@@ -26,6 +26,8 @@ import android.net.Uri;
import android.os.RemoteException;
import android.provider.UserDictionary.Words;
+import com.android.inputmethod.keyboard.ProximityInfo;
+
public class UserDictionary extends ExpandableDictionary {
private static final String[] PROJECTION_QUERY = {
@@ -38,23 +40,24 @@ public class UserDictionary extends ExpandableDictionary {
Words.FREQUENCY,
Words.LOCALE,
};
-
+
private ContentObserver mObserver;
private String mLocale;
public UserDictionary(Context context, String locale) {
super(context, Suggest.DIC_USER);
mLocale = locale;
- // Perform a managed query. The Activity will handle closing and requerying the cursor
+ // Perform a managed query. The Activity will handle closing and re-querying the cursor
// when needed.
ContentResolver cres = context.getContentResolver();
-
- cres.registerContentObserver(Words.CONTENT_URI, true, mObserver = new ContentObserver(null) {
+
+ mObserver = new ContentObserver(null) {
@Override
public void onChange(boolean self) {
setRequiresReload(true);
}
- });
+ };
+ cres.registerContentObserver(Words.CONTENT_URI, true, mObserver);
loadDictionary();
}
@@ -76,6 +79,17 @@ public class UserDictionary extends ExpandableDictionary {
addWords(cursor);
}
+ public boolean isEnabled() {
+ final ContentResolver cr = getContext().getContentResolver();
+ final ContentProviderClient client = cr.acquireContentProviderClient(Words.CONTENT_URI);
+ if (client != null) {
+ client.release();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
/**
* Adds a word to the dictionary and makes it persistent.
* @param word the word to add. If the word is capitalized, then the dictionary will
@@ -138,8 +152,9 @@ public class UserDictionary extends ExpandableDictionary {
}
@Override
- public synchronized void getWords(final WordComposer codes, final WordCallback callback) {
- super.getWords(codes, callback);
+ public synchronized void getWords(final WordComposer codes, final WordCallback callback,
+ final ProximityInfo proximityInfo) {
+ super.getWords(codes, callback, proximityInfo);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 6bdc0a857..1a6260a4e 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -111,35 +111,43 @@ public class Utils {
}
}
- public static boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManagerCompatWrapper imm) {
+ public static boolean hasMultipleEnabledIMEsOrSubtypes(
+ final InputMethodManagerCompatWrapper imm,
+ final boolean shouldIncludeAuxiliarySubtypes) {
final List<InputMethodInfoCompatWrapper> enabledImis = imm.getEnabledInputMethodList();
- // Filters out IMEs that have auxiliary subtypes only (including either implicitly or
- // explicitly enabled ones).
- final ArrayList<InputMethodInfoCompatWrapper> filteredImis =
- new ArrayList<InputMethodInfoCompatWrapper>();
+ // Number of the filtered IMEs
+ int filteredImisCount = 0;
- outerloop:
for (InputMethodInfoCompatWrapper imi : enabledImis) {
// We can return true immediately after we find two or more filtered IMEs.
- if (filteredImis.size() > 1) return true;
+ if (filteredImisCount > 1) return true;
final List<InputMethodSubtypeCompatWrapper> subtypes =
imm.getEnabledInputMethodSubtypeList(imi, true);
- // IMEs that have no subtypes should be included.
+ // IMEs that have no subtypes should be counted.
if (subtypes.isEmpty()) {
- filteredImis.add(imi);
+ ++filteredImisCount;
continue;
}
- // IMEs that have one or more non-auxiliary subtypes should be included.
+
+ int auxCount = 0;
for (InputMethodSubtypeCompatWrapper subtype : subtypes) {
- if (!subtype.isAuxiliary()) {
- filteredImis.add(imi);
- continue outerloop;
+ 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;
+ }
}
- return filteredImis.size() > 1
+ return filteredImisCount > 1
// imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
// input method subtype (The current IME should be LatinIME.)
|| imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
@@ -190,6 +198,18 @@ public class Utils {
}
}
+ public static boolean canBeFollowedByPeriod(final int codePoint) {
+ // TODO: Check again whether there really ain't a better way to check this.
+ // TODO: This should probably be language-dependant...
+ return Character.isLetterOrDigit(codePoint)
+ || codePoint == Keyboard.CODE_SINGLE_QUOTE
+ || codePoint == Keyboard.CODE_DOUBLE_QUOTE
+ || codePoint == Keyboard.CODE_CLOSING_PARENTHESIS
+ || codePoint == Keyboard.CODE_CLOSING_SQUARE_BRACKET
+ || codePoint == Keyboard.CODE_CLOSING_CURLY_BRACKET
+ || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
+ }
+
/* package */ static class RingCharBuffer {
private static RingCharBuffer sRingCharBuffer = new RingCharBuffer();
private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC';
@@ -546,11 +566,11 @@ public class Utils {
}
}
- public static int getKeyboardMode(EditorInfo attribute) {
- if (attribute == null)
+ public static int getKeyboardMode(EditorInfo editorInfo) {
+ if (editorInfo == null)
return KeyboardId.MODE_TEXT;
- final int inputType = attribute.inputType;
+ final int inputType = editorInfo.inputType;
final int variation = inputType & InputType.TYPE_MASK_VARIATION;
switch (inputType & InputType.TYPE_MASK_CLASS) {
@@ -587,11 +607,11 @@ public class Utils {
}
public static boolean inPrivateImeOptions(String packageName, String key,
- EditorInfo attribute) {
- if (attribute == null)
+ EditorInfo editorInfo) {
+ if (editorInfo == null)
return false;
return containsInCsv(packageName != null ? packageName + "." + key : key,
- attribute.privateImeOptions);
+ editorInfo.privateImeOptions);
}
/**
diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
index 4377373d2..639c96681 100644
--- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
+++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
@@ -21,6 +21,8 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
+import com.android.inputmethod.keyboard.ProximityInfo;
+
import java.util.HashMap;
public class WhitelistDictionary extends Dictionary {
@@ -89,7 +91,8 @@ public class WhitelistDictionary extends Dictionary {
// Not used for WhitelistDictionary. We use getWhitelistedWord() in Suggest.java instead
@Override
- public void getWords(WordComposer composer, WordCallback callback) {
+ public void getWords(final WordComposer composer, final WordCallback callback,
+ final ProximityInfo proximityInfo) {
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
new file mode 100644
index 000000000..44e999572
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -0,0 +1,167 @@
+/*
+ * 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.latin.spellcheck;
+
+import android.content.res.Resources;
+import android.service.textservice.SpellCheckerService;
+import android.service.textservice.SpellCheckerService.Session;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+import com.android.inputmethod.compat.ArraysCompatUtils;
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.Dictionary.DataType;
+import com.android.inputmethod.latin.Dictionary.WordCallback;
+import com.android.inputmethod.latin.DictionaryFactory;
+import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.WordComposer;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Service for spell checking, using LatinIME's dictionaries and mechanisms.
+ */
+public class AndroidSpellCheckerService extends SpellCheckerService {
+ private static final String TAG = AndroidSpellCheckerService.class.getSimpleName();
+ private static final boolean DBG = true;
+
+ private final static String[] emptyArray = new String[0];
+ private final ProximityInfo mProximityInfo = ProximityInfo.getSpellCheckerProximityInfo();
+ private final Map<String, Dictionary> mDictionaries =
+ Collections.synchronizedMap(new TreeMap<String, Dictionary>());
+
+ @Override
+ public Session createSession() {
+ return new AndroidSpellCheckerSession();
+ }
+
+ private static class SuggestionsGatherer implements WordCallback {
+ private final int DEFAULT_SUGGESTION_LENGTH = 16;
+ private final String[] mSuggestions;
+ private final int[] mScores;
+ private final int mMaxLength;
+ private int mLength = 0;
+
+ SuggestionsGatherer(final int maxLength) {
+ mMaxLength = maxLength;
+ mSuggestions = new String[mMaxLength];
+ mScores = new int[mMaxLength];
+ }
+
+ @Override
+ synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score,
+ int dicTypeId, DataType dataType) {
+ final int positionIndex = ArraysCompatUtils.binarySearch(mScores, 0, mLength, score);
+ // binarySearch returns the index if the element exists, and -<insertion index> - 1
+ // if it doesn't. See documentation for binarySearch.
+ final int insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1;
+
+ if (mLength < mMaxLength) {
+ final int copyLen = mLength - insertIndex;
+ ++mLength;
+ System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen);
+ System.arraycopy(mSuggestions, insertIndex, mSuggestions, insertIndex + 1, copyLen);
+ } else {
+ if (insertIndex == 0) return true;
+ System.arraycopy(mScores, 1, mScores, 0, insertIndex);
+ System.arraycopy(mSuggestions, 1, mSuggestions, 0, insertIndex);
+ }
+ mScores[insertIndex] = score;
+ mSuggestions[insertIndex] = new String(word, wordOffset, wordLength);
+
+ return true;
+ }
+
+ public String[] getGatheredSuggestions() {
+ if (0 == mLength) return null;
+
+ final String[] results = new String[mLength];
+ for (int i = mLength - 1; i >= 0; --i) {
+ results[mLength - i - 1] = mSuggestions[i];
+ }
+ return results;
+ }
+ }
+
+ private Dictionary getDictionary(final String locale) {
+ Dictionary dictionary = mDictionaries.get(locale);
+ if (null == dictionary) {
+ final Resources resources = getResources();
+ final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources);
+ final Locale localeObject = Utils.constructLocaleFromString(locale);
+ dictionary = DictionaryFactory.createDictionaryFromManager(this, localeObject,
+ fallbackResourceId);
+ mDictionaries.put(locale, dictionary);
+ }
+ return dictionary;
+ }
+
+ private class AndroidSpellCheckerSession extends Session {
+ @Override
+ public void onCreate() {
+ }
+
+ // Note : this must be reentrant
+ /**
+ * Gets a list of suggestions for a specific string. This returns a list of possible
+ * corrections for the text passed as an arguments. It may split or group words, and
+ * even perform grammatical analysis.
+ */
+ @Override
+ public SuggestionsInfo onGetSuggestions(final TextInfo textInfo,
+ final int suggestionsLimit) {
+ final String locale = getLocale();
+ final Dictionary dictionary = getDictionary(locale);
+ final String text = textInfo.getText();
+
+ final SuggestionsGatherer suggestionsGatherer =
+ new SuggestionsGatherer(suggestionsLimit);
+ final WordComposer composer = new WordComposer();
+ final int length = text.length();
+ for (int i = 0; i < length; ++i) {
+ final int character = text.codePointAt(i);
+ final int proximityIndex = SpellCheckerProximityInfo.getIndexOf(character);
+ final int[] proximities;
+ if (-1 == proximityIndex) {
+ proximities = new int[] { character };
+ } else {
+ proximities = Arrays.copyOfRange(SpellCheckerProximityInfo.PROXIMITY,
+ proximityIndex, proximityIndex + SpellCheckerProximityInfo.ROW_SIZE);
+ }
+ composer.add(character, proximities,
+ WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
+ }
+ dictionary.getWords(composer, suggestionsGatherer, mProximityInfo);
+ final boolean isInDict = dictionary.isValidWord(text);
+ final String[] suggestions = suggestionsGatherer.getGatheredSuggestions();
+
+ final int flags =
+ (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY : 0)
+ | (null != suggestions
+ ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0);
+ return new SuggestionsInfo(flags, suggestions);
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java
deleted file mode 100644
index 63c6d69d7..000000000
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.latin.spellcheck;
-
-import android.content.Context;
-import android.content.res.Resources;
-
-import com.android.inputmethod.compat.ArraysCompatUtils;
-import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.latin.Dictionary.DataType;
-import com.android.inputmethod.latin.Dictionary.WordCallback;
-import com.android.inputmethod.latin.DictionaryFactory;
-import com.android.inputmethod.latin.Utils;
-import com.android.inputmethod.latin.WordComposer;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Implements spell checking methods.
- */
-public class SpellChecker {
-
- public final Dictionary mDictionary;
-
- public SpellChecker(final Context context, final Locale locale) {
- final Resources resources = context.getResources();
- final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources);
- mDictionary = DictionaryFactory.createDictionaryFromManager(context, locale,
- fallbackResourceId);
- }
-
- // Note : this must be reentrant
- /**
- * Finds out whether a word is in the dictionary or not.
- *
- * @param text the sequence containing the word to check for.
- * @param start the index of the first character of the word in text.
- * @param end the index of the next-to-last character in text.
- * @return true if the word is in the dictionary, false otherwise.
- */
- public boolean isCorrect(final CharSequence text, final int start, final int end) {
- return mDictionary.isValidWord(text.subSequence(start, end));
- }
-
- private static class SuggestionsGatherer implements WordCallback {
- private final int DEFAULT_SUGGESTION_LENGTH = 16;
- private final List<String> mSuggestions = new LinkedList<String>();
- private int[] mScores = new int[DEFAULT_SUGGESTION_LENGTH];
- private int mLength = 0;
-
- @Override
- synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score,
- int dicTypeId, DataType dataType) {
- if (mLength >= mScores.length) {
- final int newLength = mScores.length * 2;
- mScores = new int[newLength];
- }
- final int positionIndex = ArraysCompatUtils.binarySearch(mScores, 0, mLength, score);
- // binarySearch returns the index if the element exists, and -<insertion index> - 1
- // if it doesn't. See documentation for binarySearch.
- final int insertionIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1;
- System.arraycopy(mScores, insertionIndex, mScores, insertionIndex + 1,
- mLength - insertionIndex);
- mLength += 1;
- mScores[insertionIndex] = score;
- mSuggestions.add(insertionIndex, new String(word, wordOffset, wordLength));
- return true;
- }
-
- public List<String> getGatheredSuggestions() {
- return mSuggestions;
- }
- }
-
- // Note : this must be reentrant
- /**
- * Gets a list of suggestions for a specific string.
- *
- * This returns a list of possible corrections for the text passed as an
- * arguments. It may split or group words, and even perform grammatical
- * analysis.
- *
- * @param text the sequence containing the word to check for.
- * @param start the index of the first character of the word in text.
- * @param end the index of the next-to-last character in text.
- * @return a list of possible suggestions to replace the text.
- */
- public List<String> getSuggestions(final CharSequence text, final int start, final int end) {
- final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer();
- final WordComposer composer = new WordComposer();
- for (int i = start; i < end; ++i) {
- int character = text.charAt(i);
- composer.add(character, new int[] { character },
- WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
- }
- mDictionary.getWords(composer, suggestionsGatherer);
- return suggestionsGatherer.getGatheredSuggestions();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
new file mode 100644
index 000000000..abcf7e52a
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
@@ -0,0 +1,94 @@
+/*
+ * 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.latin.spellcheck;
+
+import com.android.inputmethod.keyboard.KeyDetector;
+import com.android.inputmethod.keyboard.ProximityInfo;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+public class SpellCheckerProximityInfo {
+ final private static int NUL = KeyDetector.NOT_A_CODE;
+
+ // This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside
+ // native code - this value is passed at creation of the binary object and reused
+ // as the size of the passed array afterwards so they can't be different.
+ final public static int ROW_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE;
+
+ // This is a map from the code point to the index in the PROXIMITY array.
+ // At the time the native code to read the binary dictionary needs the proximity info be passed
+ // as a flat array spaced by MAX_PROXIMITY_CHARS_SIZE columns, one for each input character.
+ // Since we need to build such an array, we want to be able to search in our big proximity data
+ // quickly by character, and a map is probably the best way to do this.
+ final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>();
+
+ // The proximity here is the union of
+ // - the proximity for a QWERTY keyboard.
+ // - the proximity for an AZERTY keyboard.
+ // - the proximity for a QWERTZ keyboard.
+ // ...plus, add all characters in the ('a', 'e', 'i', 'o', 'u') set to each other.
+ //
+ // The reasoning behind this construction is, almost any alphabetic text we may want
+ // to spell check has been entered with one of the keyboards above. Also, specifically
+ // to English, many spelling errors consist of the last vowel of the word being wrong
+ // because in English vowels tend to merge with each other in pronunciation.
+ final public static int[] PROXIMITY = {
+ 'q', 'w', 's', 'a', 'z', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'w', 'q', 'a', 's', 'd', 'e', 'x', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'e', 'w', 's', 'd', 'f', 'r', 'a', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL,
+ 'r', 'e', 'd', 'f', 'g', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 't', 'r', 'f', 'g', 'h', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'y', 't', 'g', 'h', 'j', 'u', 'a', 's', 'd', 'x', NUL, NUL, NUL, NUL, NUL, NUL,
+ 'u', 'y', 'h', 'j', 'k', 'i', 'a', 'e', 'o', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'i', 'u', 'j', 'k', 'l', 'o', 'a', 'e', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'o', 'i', 'k', 'l', 'p', 'a', 'e', 'u', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'p', 'o', 'l', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+
+ 'a', 'z', 'x', 's', 'w', 'q', 'e', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL,
+ 's', 'q', 'a', 'z', 'x', 'c', 'd', 'e', 'w', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'd', 'w', 's', 'x', 'c', 'v', 'f', 'r', 'e', 'w', NUL, NUL, NUL, NUL, NUL, NUL,
+ 'f', 'e', 'd', 'c', 'v', 'b', 'g', 't', 'r', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'g', 'r', 'f', 'v', 'b', 'n', 'h', 'y', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'h', 't', 'g', 'b', 'n', 'm', 'j', 'u', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'j', 'y', 'h', 'n', 'm', 'k', 'i', 'u', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'k', 'u', 'j', 'm', 'l', 'o', 'i', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'l', 'i', 'k', 'p', 'o', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+
+ 'z', 'a', 's', 'd', 'x', 't', 'g', 'h', 'j', 'u', 'q', 'e', NUL, NUL, NUL, NUL,
+ 'x', 'z', 'a', 's', 'd', 'c', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'c', 'x', 's', 'd', 'f', 'v', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'v', 'c', 'd', 'f', 'g', 'b', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'b', 'v', 'f', 'g', 'h', 'n', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'n', 'b', 'g', 'h', 'j', 'm', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ 'm', 'n', 'h', 'j', 'k', 'l', 'o', 'p', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ };
+ static {
+ for (int i = 0; i < PROXIMITY.length; i += ROW_SIZE) {
+ if (NUL != PROXIMITY[i]) INDICES.put(PROXIMITY[i], i);
+ }
+ }
+ public static int getIndexOf(int characterCode) {
+ final Integer result = INDICES.get(characterCode);
+ if (null == result) return -1;
+ return result;
+ }
+}