diff options
Diffstat (limited to 'java/src')
26 files changed, 483 insertions, 414 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java index 4a2542dec..dcebb1c04 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java @@ -116,8 +116,8 @@ public class AccessibilityUtils { * @return {@code true} if the device should not speak text (eg. * non-control) characters */ - public boolean shouldObscureInput(EditorInfo attribute) { - if (attribute == null) + public boolean shouldObscureInput(EditorInfo editorInfo) { + if (editorInfo == null) return false; // Always speak if the user is listening through headphones. @@ -125,7 +125,7 @@ public class AccessibilityUtils { return false; // Don't speak if the IME is connected to a password field. - return InputTypeCompatUtils.isPasswordInputType(attribute.inputType); + return InputTypeCompatUtils.isPasswordInputType(editorInfo.inputType); } /** @@ -159,11 +159,11 @@ public class AccessibilityUtils { * Handles speaking the "connect a headset to hear passwords" notification * when connecting to a password field. * - * @param attribute The input connection's editor info attribute. + * @param editorInfo The input connection's editor info attribute. * @param restarting Whether the connection is being restarted. */ - public void onStartInputViewInternal(EditorInfo attribute, boolean restarting) { - if (shouldObscureInput(attribute)) { + public void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) { + if (shouldObscureInput(editorInfo)) { final CharSequence text = mContext.getText(R.string.spoken_use_headphones); speak(text); } diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java index 51dc4cd37..0e5f8c80a 100644 --- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java +++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java @@ -153,8 +153,7 @@ public class InputMethodManagerCompatWrapper { return Utils.getInputMethodInfo(this, mLatinImePackageName); } - @SuppressWarnings("unused") - private InputMethodSubtypeCompatWrapper getLastResortSubtype(String mode) { + private static InputMethodSubtypeCompatWrapper getLastResortSubtype(String mode) { if (VOICE_MODE.equals(mode) && !FORCE_ENABLE_VOICE_EVEN_WITH_NO_VOICE_SUBTYPES) return null; Locale inputLocale = SubtypeSwitcher.getInstance().getInputLocale(); diff --git a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java index 3f8c2ef8f..a013ebca9 100644 --- a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java +++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java @@ -695,12 +695,12 @@ public class VoiceProxy implements VoiceInput.UiListener { && !mVoiceInput.isBlacklistedField(fieldContext); } - private boolean shouldShowVoiceButton(FieldContext fieldContext, EditorInfo attribute) { + private boolean shouldShowVoiceButton(FieldContext fieldContext, EditorInfo editorInfo) { @SuppressWarnings("deprecation") final boolean noMic = Utils.inPrivateImeOptions(null, - LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, attribute) + LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo) || Utils.inPrivateImeOptions(mService.getPackageName(), - LatinIME.IME_OPTION_NO_MICROPHONE, attribute); + LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo); return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext) && !noMic && SpeechRecognizer.isRecognitionAvailable(mService); } @@ -709,7 +709,7 @@ public class VoiceProxy implements VoiceInput.UiListener { return SpeechRecognizer.isRecognitionAvailable(context); } - public void loadSettings(EditorInfo attribute, SharedPreferences sp) { + public void loadSettings(EditorInfo editorInfo, SharedPreferences sp) { if (!VOICE_INSTALLED) { return; } @@ -723,7 +723,7 @@ public class VoiceProxy implements VoiceInput.UiListener { 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); + && shouldShowVoiceButton(makeFieldContext(), editorInfo); mVoiceButtonOnPrimary = voiceMode.equals(mService.getString(R.string.voice_mode_main)); } diff --git a/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java b/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java index 3c79cc218..fd2cf3d25 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java +++ b/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java @@ -43,10 +43,10 @@ public class FieldContext { Bundle mFieldInfo; - public FieldContext(InputConnection conn, EditorInfo info, + public FieldContext(InputConnection conn, EditorInfo editorInfo, String selectedLanguage, String[] enabledLanguages) { mFieldInfo = new Bundle(); - addEditorInfoToBundle(info, mFieldInfo); + addEditorInfoToBundle(editorInfo, mFieldInfo); addInputConnectionToBundle(conn, mFieldInfo); addLanguageInfoToBundle(selectedLanguage, enabledLanguages, mFieldInfo); if (DBG) Log.i("FieldContext", "Bundle = " + mFieldInfo.toString()); diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index f1ae0b313..15c41a9f2 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -51,22 +51,22 @@ public class Key { public final CharSequence mLabel; /** Hint label to display on the key in conjunction with the label */ public final CharSequence mHintLabel; - /** Option of the label */ - 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; - private static final int LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO = 0x80; - private static final int LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO = 0x100; - 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; - private static final int LABEL_OPTION_AUTO_X_SCALE = 0x4000; + /** Flags of the label */ + private final int mLabelFlags; + private static final int LABEL_FLAGS_ALIGN_LEFT = 0x01; + private static final int LABEL_FLAGS_ALIGN_RIGHT = 0x02; + private static final int LABEL_FLAGS_ALIGN_LEFT_OF_CENTER = 0x08; + private static final int LABEL_FLAGS_LARGE_LETTER = 0x10; + private static final int LABEL_FLAGS_FONT_NORMAL = 0x20; + private static final int LABEL_FLAGS_FONT_MONO_SPACE = 0x40; + private static final int LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO = 0x80; + private static final int LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO = 0x100; + private static final int LABEL_FLAGS_HAS_POPUP_HINT = 0x200; + private static final int LABEL_FLAGS_HAS_UPPERCASE_LETTER = 0x400; + private static final int LABEL_FLAGS_HAS_HINT_LABEL = 0x800; + private static final int LABEL_FLAGS_WITH_ICON_LEFT = 0x1000; + private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000; + private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000; /** Icon to display instead of a label. Icon takes precedence over a label */ private Drawable mIcon; @@ -105,8 +105,9 @@ public class Key { public static final int BACKGROUND_TYPE_ACTION = 2; public static final int BACKGROUND_TYPE_STICKY = 3; - /** Whether this key repeats itself when held down */ - public final boolean mRepeatable; + private final int mActionFlags; + private static final int ACTION_FLAGS_IS_REPEATABLE = 0x01; + private static final int ACTION_FLAGS_NO_KEY_PREVIEW = 0x02; /** The current pressed state of this key */ private boolean mPressed; @@ -181,9 +182,9 @@ public class Key { mVisualInsetsLeft = mVisualInsetsRight = 0; mWidth = width - mHorizontalGap; mHintLabel = hintLabel; - mLabelOption = 0; + mLabelFlags = 0; mBackgroundType = BACKGROUND_TYPE_NORMAL; - mRepeatable = false; + mActionFlags = 0; mMoreKeys = null; mMaxMoreKeysColumn = 0; mLabel = label; @@ -223,7 +224,7 @@ public class Key { if (style == null) throw new ParseException("Unknown key style: " + styleName, parser); } else { - style = keyStyles.getEmptyKeyStyle(); + style = KeyStyles.getEmptyKeyStyle(); } final float keyXPos = row.getKeyX(keyAttr); @@ -254,8 +255,7 @@ public class Key { mBackgroundType = style.getInt(keyAttr, R.styleable.Keyboard_Key_backgroundType, BACKGROUND_TYPE_NORMAL); - mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false); - mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true); + mActionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags, 0); final KeyboardIconsSet iconsSet = params.mIconsSet; mVisualInsetsLeft = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr, @@ -275,7 +275,7 @@ public class Key { mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel); - mLabelOption = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption, 0); + mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags, 0); mOutputText = style.getText(keyAttr, R.styleable.Keyboard_Key_keyOutputText); // Choose the first letter of the label as primary code if not // specified. @@ -317,11 +317,19 @@ public class Key { return false; } + public boolean isRepeatable() { + return (mActionFlags & ACTION_FLAGS_IS_REPEATABLE) != 0; + } + + public boolean noKeyPreview() { + return (mActionFlags & ACTION_FLAGS_NO_KEY_PREVIEW) != 0; + } + public Typeface selectTypeface(Typeface defaultTypeface) { // TODO: Handle "bold" here too? - if ((mLabelOption & LABEL_OPTION_FONT_NORMAL) != 0) { + if ((mLabelFlags & LABEL_FLAGS_FONT_NORMAL) != 0) { return Typeface.DEFAULT; - } else if ((mLabelOption & LABEL_OPTION_FONT_MONO_SPACE) != 0) { + } else if ((mLabelFlags & LABEL_FLAGS_FONT_MONO_SPACE) != 0) { return Typeface.MONOSPACE; } else { return defaultTypeface; @@ -330,12 +338,12 @@ public class Key { public int selectTextSize(int letter, int largeLetter, int label, int hintLabel) { if (mLabel.length() > 1 - && (mLabelOption & (LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO - | LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO)) == 0) { + && (mLabelFlags & (LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO + | LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO)) == 0) { return label; - } else if ((mLabelOption & LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO) != 0) { + } else if ((mLabelFlags & LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO) != 0) { return hintLabel; - } else if ((mLabelOption & LABEL_OPTION_LARGE_LETTER) != 0) { + } else if ((mLabelFlags & LABEL_FLAGS_LARGE_LETTER) != 0) { return largeLetter; } else { return letter; @@ -343,19 +351,19 @@ public class Key { } public boolean isAlignLeft() { - return (mLabelOption & LABEL_OPTION_ALIGN_LEFT) != 0; + return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT) != 0; } public boolean isAlignRight() { - return (mLabelOption & LABEL_OPTION_ALIGN_RIGHT) != 0; + return (mLabelFlags & LABEL_FLAGS_ALIGN_RIGHT) != 0; } public boolean isAlignLeftOfCenter() { - return (mLabelOption & LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0; + return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT_OF_CENTER) != 0; } public boolean hasPopupHint() { - return (mLabelOption & LABEL_OPTION_HAS_POPUP_HINT) != 0; + return (mLabelFlags & LABEL_FLAGS_HAS_POPUP_HINT) != 0; } public void setNeedsSpecialPopupHint(boolean needsSpecialPopupHint) { @@ -367,23 +375,23 @@ public class Key { } public boolean hasUppercaseLetter() { - return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0; + return (mLabelFlags & LABEL_FLAGS_HAS_UPPERCASE_LETTER) != 0; } public boolean hasHintLabel() { - return (mLabelOption & LABEL_OPTION_HAS_HINT_LABEL) != 0; + return (mLabelFlags & LABEL_FLAGS_HAS_HINT_LABEL) != 0; } public boolean hasLabelWithIconLeft() { - return (mLabelOption & LABEL_OPTION_WITH_ICON_LEFT) != 0; + return (mLabelFlags & LABEL_FLAGS_WITH_ICON_LEFT) != 0; } public boolean hasLabelWithIconRight() { - return (mLabelOption & LABEL_OPTION_WITH_ICON_RIGHT) != 0; + return (mLabelFlags & LABEL_FLAGS_WITH_ICON_RIGHT) != 0; } public boolean needsXScale() { - return (mLabelOption & LABEL_OPTION_AUTO_X_SCALE) != 0; + return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0; } public Drawable getIcon() { diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 4578507fc..4a28bf1de 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -75,7 +75,6 @@ public class Keyboard { public static final int CODE_DELETE = -5; public static final int CODE_SETTINGS = -6; public static final int CODE_SHORTCUT = -7; - public static final int CODE_HAPTIC_AND_AUDIO_FEEDBACK_ONLY = -98; // Code value representing the code is not specified. public static final int CODE_UNSPECIFIED = -99; @@ -218,10 +217,6 @@ public class Keyboard { return mId.isPhoneKeyboard(); } - public boolean isNumberKeyboard() { - return mId.isNumberKeyboard(); - } - public CharSequence adjustLabelCase(CharSequence label) { if (isShiftedOrShiftLocked() && !TextUtils.isEmpty(label) && label.length() < 3 && Character.isLowerCase(label.charAt(0))) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 2e4988fb0..840857778 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -58,15 +58,15 @@ public class KeyboardId { public final int mImeAction; public final String mXmlName; - public final EditorInfo mAttribute; + public final EditorInfo mEditorInfo; private final int mHashCode; public KeyboardId(String xmlName, int xmlId, Locale locale, int orientation, int width, - int mode, EditorInfo attribute, boolean hasSettingsKey, int f2KeyMode, + int mode, EditorInfo editorInfo, boolean hasSettingsKey, int f2KeyMode, boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) { - final int inputType = (attribute != null) ? attribute.inputType : 0; - final int imeOptions = (attribute != null) ? attribute.imeOptions : 0; + final int inputType = (editorInfo != null) ? editorInfo.inputType : 0; + final int imeOptions = (editorInfo != null) ? editorInfo.imeOptions : 0; this.mLocale = locale; this.mOrientation = orientation; this.mWidth = width; @@ -89,7 +89,7 @@ public class KeyboardId { EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); this.mXmlName = xmlName; - this.mAttribute = attribute; + this.mEditorInfo = editorInfo; this.mHashCode = Arrays.hashCode(new Object[] { locale, @@ -109,7 +109,7 @@ public class KeyboardId { } public KeyboardId cloneWithNewXml(String xmlName, int xmlId) { - return new KeyboardId(xmlName, xmlId, mLocale, mOrientation, mWidth, mMode, mAttribute, + return new KeyboardId(xmlName, xmlId, mLocale, mOrientation, mWidth, mMode, mEditorInfo, false, F2KEY_MODE_NONE, false, false, false); } @@ -133,10 +133,6 @@ public class KeyboardId { return mXmlId == R.xml.kbd_phone_shift; } - public boolean isNumberKeyboard() { - return mMode == MODE_NUMBER; - } - @Override public boolean equals(Object other) { return other instanceof KeyboardId && equals((KeyboardId) other); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index ac718fc62..4d30077e3 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -666,11 +666,16 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha return mKeyboardView == null ? 0 : mKeyboardView.getPointerCount(); } + private boolean mPrevMainKeyboardWasShiftLocked; + private void toggleKeyboardMode() { if (mCurrentId.equals(mMainKeyboardId)) { + mPrevMainKeyboardWasShiftLocked = isShiftLocked(); setKeyboard(getKeyboard(mSymbolsKeyboardId)); } else { setKeyboard(getKeyboard(mMainKeyboardId)); + setShiftLocked(mPrevMainKeyboardWasShiftLocked); + mPrevMainKeyboardWasShiftLocked = false; } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 04e672590..3ce184941 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -906,12 +906,16 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { int previewX = keyDrawX - (previewWidth - keyDrawWidth) / 2 + params.mCoordinates[0]; final int previewY = key.mY - previewHeight + params.mCoordinates[1] + params.mPreviewOffset; - if (previewX < 0 && params.mPreviewLeftBackground != null) { - previewText.setBackgroundDrawable(params.mPreviewLeftBackground); + if (previewX < 0) { previewX = 0; - } else if (previewX + previewWidth > getWidth() && params.mPreviewRightBackground != null) { - previewText.setBackgroundDrawable(params.mPreviewRightBackground); + if (params.mPreviewLeftBackground != null) { + previewText.setBackgroundDrawable(params.mPreviewLeftBackground); + } + } else if (previewX > getWidth() - previewWidth) { previewX = getWidth() - previewWidth; + if (params.mPreviewRightBackground != null) { + previewText.setBackgroundDrawable(params.mPreviewRightBackground); + } } // Set the preview background state diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 6ce3876b6..abf24ead2 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -21,7 +21,6 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Canvas; import android.os.Message; -import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; @@ -264,20 +263,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke return mKeyTimerHandler; } - @Override - public void setKeyPreviewPopupEnabled(boolean previewEnabled, int 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); - } - /** * Attaches a keyboard to this view. The keyboard can be switched at any time and the * view will re-layout itself to accommodate the keyboard. @@ -349,9 +334,11 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke // When shift key is double tapped, the first tap is correctly processed as usual tap. And // the second tap is treated as this double tap event, so that we need not mark tracker // calling setAlreadyProcessed() nor remove the tracker from mPointerQueue. - final int primaryCode = ignore ? Keyboard.CODE_HAPTIC_AND_AUDIO_FEEDBACK_ONLY - : Keyboard.CODE_CAPSLOCK; - invokeCodeInput(primaryCode); + if (ignore) { + mKeyboardActionListener.onCustomRequest(LatinIME.CODE_HAPTIC_AND_AUDIO_FEEDBACK); + } else { + mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0); + } } // This default implementation returns a more keys panel. @@ -463,8 +450,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke this, this, pointX, pointY, mMoreKeysWindow, getKeyboardActionListener()); final int translatedX = moreKeysPanel.translateX(tracker.getLastX()); final int translatedY = moreKeysPanel.translateY(tracker.getLastY()); - tracker.onShowMoreKeysPanel( - translatedX, translatedY, SystemClock.uptimeMillis(), moreKeysPanel); + tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel); dimEntireKeyboard(true); return true; } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 198e06aab..fb4021048 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard; import android.content.Context; import android.content.res.Resources; +import android.os.SystemClock; import android.util.Log; import android.view.MotionEvent; import android.widget.TextView; @@ -342,7 +343,7 @@ public class PointerTracker { private void setPressedKeyGraphics(int keyIndex) { final Key key = getKey(keyIndex); if (key != null && key.isEnabled()) { - if (isKeyPreviewRequired(key)) { + if (!key.noKeyPreview()) { mDrawingProxy.showKeyPreview(keyIndex, this); } key.onPressed(); @@ -350,18 +351,6 @@ public class PointerTracker { } } - // The modifier key, such as shift key, should not show its key preview. - private static boolean isKeyPreviewRequired(Key key) { - final int code = key.mCode; - // TODO: Stop hard-coding these key codes here, and add a new key attribute of a key. - if (code == Keyboard.CODE_SPACE || code == Keyboard.CODE_ENTER - || code == Keyboard.CODE_DELETE || isModifierCode(code) - || code == Keyboard.CODE_SETTINGS || code == Keyboard.CODE_SHORTCUT) { - return false; - } - return true; - } - public int getLastX() { return mLastX; } @@ -618,9 +607,9 @@ public class PointerTracker { } } - public void onShowMoreKeysPanel(int x, int y, long eventTime, KeyEventHandler handler) { + public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) { onLongPressed(); - onDownEvent(x, y, eventTime, handler); + onDownEvent(x, y, SystemClock.uptimeMillis(), handler); mIsShowingMoreKeysPanel = true; } @@ -658,7 +647,7 @@ public class PointerTracker { private void startRepeatKey(int keyIndex) { final Key key = getKey(keyIndex); - if (key != null && key.mRepeatable) { + if (key != null && key.isRepeatable()) { onRepeatKey(keyIndex); mTimerProxy.startKeyRepeatTimer(sDelayBeforeKeyRepeatStart, keyIndex, this); mIsRepeatableKey = true; diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 2a25d0ca7..6c5c3e7be 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -75,16 +75,16 @@ public class ProximityInfo { return spellCheckerProximityInfo; } - private int mNativeProximityInfo; + private long mNativeProximityInfo; static { Utils.loadNativeLibrary(); } - private native int setProximityInfoNative(int maxProximityCharsSize, int displayWidth, + private native long setProximityInfoNative(int maxProximityCharsSize, int displayWidth, int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray, int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, int[] keyWidths, int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii); - private native void releaseProximityInfoNative(int nativeProximityInfo); + private native void releaseProximityInfoNative(long nativeProximityInfo); private final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth, int keyboardHeight, List<Key> keys, @@ -157,7 +157,7 @@ public class ProximityInfo { } } - public int getNativeProximityInfo() { + public long getNativeProximityInfo() { return mNativeProximityInfo; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index b385b7a04..565edb901 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -40,7 +40,6 @@ public class KeyStyles { public CharSequence getText(TypedArray a, int index); public int getInt(TypedArray a, int index, int defaultValue); public int getFlag(TypedArray a, int index, int defaultValue); - public boolean getBoolean(TypedArray a, int index, boolean defaultValue); } /* package */ static class EmptyKeyStyle implements KeyStyle { @@ -68,11 +67,6 @@ public class KeyStyles { return a.getInt(index, defaultValue); } - @Override - public boolean getBoolean(TypedArray a, int index, boolean defaultValue) { - return a.getBoolean(index, defaultValue); - } - protected static CharSequence[] parseTextArray(TypedArray a, int index) { if (!a.hasValue(index)) return null; @@ -151,12 +145,6 @@ public class KeyStyles { return super.getFlag(a, index, defaultValue) | (value != null ? value : 0); } - @Override - public boolean getBoolean(TypedArray a, int index, boolean defaultValue) { - final Boolean value = (Boolean)mAttributes.get(index); - return super.getBoolean(a, index, (value != null) ? value : defaultValue); - } - private DeclaredKeyStyle() { super(); } @@ -168,14 +156,13 @@ public class KeyStyles { readText(keyAttr, R.styleable.Keyboard_Key_keyOutputText); readText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); readTextArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); - readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption); + readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags); readInt(keyAttr, R.styleable.Keyboard_Key_keyIcon); readInt(keyAttr, R.styleable.Keyboard_Key_keyIconPreview); readInt(keyAttr, R.styleable.Keyboard_Key_keyIconShifted); readInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn); readInt(keyAttr, R.styleable.Keyboard_Key_backgroundType); - readBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable); - readBoolean(keyAttr, R.styleable.Keyboard_Key_enabled); + readFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags); } private void readText(TypedArray a, int index) { @@ -194,11 +181,6 @@ public class KeyStyles { mAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0)); } - private void readBoolean(TypedArray a, int index) { - if (a.hasValue(index)) - mAttributes.put(index, a.getBoolean(index, false)); - } - private void readTextArray(TypedArray a, int index) { final CharSequence[] value = parseTextArray(a, index); if (value != null) @@ -235,7 +217,7 @@ public class KeyStyles { return mStyles.get(styleName); } - public KeyStyle getEmptyKeyStyle() { + public static KeyStyle getEmptyKeyStyle() { return EMPTY_KEY_STYLE; } } diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java index 485ec511f..cd066a3d1 100644 --- a/java/src/com/android/inputmethod/latin/AutoCorrection.java +++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java @@ -98,7 +98,7 @@ public class AutoCorrection { return whiteListedWord != null; } - private boolean hasAutoCorrectionForTypedWord(Map<String, Dictionary> dictionaries, + private static boolean hasAutoCorrectionForTypedWord(Map<String, Dictionary> dictionaries, WordComposer wordComposer, ArrayList<CharSequence> suggestions, CharSequence typedWord, int correctionMode) { if (TextUtils.isEmpty(typedWord)) return false; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index b9fd57434..f0e56d346 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -46,7 +46,7 @@ public class BinaryDictionary extends Dictionary { private static final int TYPED_LETTER_MULTIPLIER = 2; private int mDicTypeId; - private int mNativeDict; + private long mNativeDict; private final int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_PROXIMITY_CHARS_SIZE]; private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS]; private final char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS]; @@ -107,15 +107,15 @@ public class BinaryDictionary extends Dictionary { Utils.loadNativeLibrary(); } - private native int openNative(String sourceDir, long dictOffset, long dictSize, + private native long openNative(String sourceDir, long dictOffset, long dictSize, int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, int maxAlternatives); - private native void closeNative(int dict); - private native boolean isValidWordNative(int nativeData, char[] word, int wordLength); - private native int getSuggestionsNative(int dict, int proximityInfo, int[] xCoordinates, + private native void closeNative(long dict); + private native boolean isValidWordNative(long dict, char[] word, int wordLength); + private native int getSuggestionsNative(long dict, long proximityInfo, int[] xCoordinates, int[] yCoordinates, int[] inputCodes, int codesSize, int flags, char[] outputChars, int[] scores); - private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength, + private native int getBigramsNative(long dict, char[] prevWord, int prevWordLength, int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores, int maxWordLength, int maxBigrams, int maxAlternatives); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 7ba7f7d27..f4a1c6a1d 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -157,6 +157,22 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar SUGGESTION_VISIBILILTY_HIDE_VALUE }; + // 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. + // Weak space: a space that should be swapped only by suggestion strip punctuation. + // Double space: the state where the user pressed space twice quickly, which LatinIME + // resolved as period-space. Undoing this converts the period to a space. + // Swap punctuation: the state where a (weak or magic) space and a punctuation from the + // suggestion strip have just been swapped. Undoing this swaps them back. + private static final int SPACE_STATE_NONE = 0; + private static final int SPACE_STATE_DOUBLE = 1; + private static final int SPACE_STATE_SWAP_PUNCTUATION = 2; + private static final int SPACE_STATE_MAGIC = 3; + private static final int SPACE_STATE_WEAK = 4; + + // Current space state of the input method. This can be any of the above constants. + private int mSpaceState; + private Settings.Values mSettingsValues; private View mExtractArea; @@ -177,7 +193,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private UserDictionary mUserDictionary; private UserBigramDictionary mUserBigramDictionary; private UserUnigramDictionary mUserUnigramDictionary; - private boolean mIsUserDictionaryAvaliable; + private boolean mIsUserDictionaryAvailable; // TODO: Create an inner class to group options and pseudo-options to improve readability. // These variables are initialized according to the {@link EditorInfo#inputType}. @@ -190,12 +206,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private WordComposer mWordComposer = new WordComposer(); private CharSequence mBestWord; private boolean mHasUncommittedTypedChars; - // 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. - // This indicates whether the last keypress resulted in processing of double space replacement - // with period-space. - private boolean mJustReplacedDoubleSpace; private int mCorrectionMode; private int mCommittedLength; @@ -399,6 +409,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private boolean mHasPendingStartInput; private boolean mHasPendingFinishInputView; private boolean mHasPendingFinishInput; + private EditorInfo mAppliedEditorInfo; public void startOrientationChanging() { removeMessages(MSG_PENDING_IMS_CALLBACK); @@ -414,18 +425,18 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mHasPendingStartInput = false; } - private void executePendingImsCallback(LatinIME latinIme, EditorInfo attribute, + private void executePendingImsCallback(LatinIME latinIme, EditorInfo editorInfo, boolean restarting) { if (mHasPendingFinishInputView) latinIme.onFinishInputViewInternal(mHasPendingFinishInput); if (mHasPendingFinishInput) latinIme.onFinishInputInternal(); if (mHasPendingStartInput) - latinIme.onStartInputInternal(attribute, restarting); + latinIme.onStartInputInternal(editorInfo, restarting); resetPendingImsCallback(); } - public void onStartInput(EditorInfo attribute, boolean restarting) { + public void onStartInput(EditorInfo editorInfo, boolean restarting) { if (hasMessages(MSG_PENDING_IMS_CALLBACK)) { // Typically this is the second onStartInput after orientation changed. mHasPendingStartInput = true; @@ -436,27 +447,28 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mPendingSuccesiveImsCallback = true; } final LatinIME latinIme = getOuterInstance(); - executePendingImsCallback(latinIme, attribute, restarting); - latinIme.onStartInputInternal(attribute, restarting); + executePendingImsCallback(latinIme, editorInfo, restarting); + latinIme.onStartInputInternal(editorInfo, restarting); } } - public void onStartInputView(EditorInfo attribute, boolean restarting) { - if (hasMessages(MSG_PENDING_IMS_CALLBACK)) { - // Typically this is the second onStartInputView after orientation changed. - resetPendingImsCallback(); - } else { - if (mPendingSuccesiveImsCallback) { - // This is the first onStartInputView after orientation changed. - mPendingSuccesiveImsCallback = false; - resetPendingImsCallback(); - sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK), - PENDING_IMS_CALLBACK_DURATION); - } - final LatinIME latinIme = getOuterInstance(); - executePendingImsCallback(latinIme, attribute, restarting); - latinIme.onStartInputViewInternal(attribute, restarting); - } + public void onStartInputView(EditorInfo editorInfo, boolean restarting) { + if (hasMessages(MSG_PENDING_IMS_CALLBACK) && editorInfo == mAppliedEditorInfo) { + // Typically this is the second onStartInputView after orientation changed. + resetPendingImsCallback(); + } else { + if (mPendingSuccesiveImsCallback) { + // This is the first onStartInputView after orientation changed. + mPendingSuccesiveImsCallback = false; + resetPendingImsCallback(); + sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK), + PENDING_IMS_CALLBACK_DURATION); + } + final LatinIME latinIme = getOuterInstance(); + executePendingImsCallback(latinIme, editorInfo, restarting); + latinIme.onStartInputViewInternal(editorInfo, restarting); + mAppliedEditorInfo = editorInfo; + } } public void onFinishInputView(boolean finishingInput) { @@ -466,6 +478,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { final LatinIME latinIme = getOuterInstance(); latinIme.onFinishInputViewInternal(finishingInput); + mAppliedEditorInfo = null; } } @@ -572,7 +585,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mUserDictionary = new UserDictionary(this, localeStr); mSuggest.setUserDictionary(mUserDictionary); - mIsUserDictionaryAvaliable = mUserDictionary.isEnabled(); + mIsUserDictionaryAvailable = mUserDictionary.isEnabled(); resetContactsDictionary(oldContactsDictionary); @@ -693,13 +706,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } @Override - public void onStartInput(EditorInfo attribute, boolean restarting) { - mHandler.onStartInput(attribute, restarting); + public void onStartInput(EditorInfo editorInfo, boolean restarting) { + mHandler.onStartInput(editorInfo, restarting); } @Override - public void onStartInputView(EditorInfo attribute, boolean restarting) { - mHandler.onStartInputView(attribute, restarting); + public void onStartInputView(EditorInfo editorInfo, boolean restarting) { + mHandler.onStartInputView(editorInfo, restarting); } @Override @@ -712,19 +725,19 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mHandler.onFinishInput(); } - private void onStartInputInternal(EditorInfo attribute, boolean restarting) { - super.onStartInput(attribute, restarting); + private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) { + super.onStartInput(editorInfo, restarting); } - private void onStartInputViewInternal(EditorInfo attribute, boolean restarting) { - super.onStartInputView(attribute, restarting); + private void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) { + super.onStartInputView(editorInfo, restarting); final KeyboardSwitcher switcher = mKeyboardSwitcher; LatinKeyboardView inputView = switcher.getKeyboardView(); if (DEBUG) { - Log.d(TAG, "onStartInputView: attribute:" + ((attribute == null) ? "none" + Log.d(TAG, "onStartInputView: editorInfo:" + ((editorInfo == null) ? "none" : String.format("inputType=0x%08x imeOptions=0x%08x", - attribute.inputType, attribute.imeOptions))); + editorInfo.inputType, editorInfo.imeOptions))); } // In landscape mode, this method gets called without the input view being created. if (inputView == null) { @@ -734,7 +747,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Forward this event to the accessibility utilities, if enabled. final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance(); if (accessUtils.isTouchExplorationEnabled()) { - accessUtils.onStartInputViewInternal(attribute, restarting); + accessUtils.onStartInputViewInternal(editorInfo, restarting); } mSubtypeSwitcher.updateParametersOnStartInputView(); @@ -745,22 +758,21 @@ 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; - final int inputType = (attribute != null) ? attribute.inputType : 0; + final int inputType = (editorInfo != null) ? editorInfo.inputType : 0; voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(inputType) || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)); // The EditorInfo might have a flag that affects fullscreen mode. // Note: This call should be done by InputMethodService? updateFullscreenMode(); - initializeInputAttributes(attribute); + initializeInputAttributes(editorInfo); inputView.closing(); mEnteredText = null; mComposingStringBuilder.setLength(0); mHasUncommittedTypedChars = false; mDeleteCount = 0; - mJustAddedMagicSpace = false; - mJustReplacedDoubleSpace = false; + mSpaceState = SPACE_STATE_NONE; loadSettings(); updateCorrectionMode(); @@ -769,12 +781,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) { mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); } - mVoiceProxy.loadSettings(attribute, mPrefs); + mVoiceProxy.loadSettings(editorInfo, mPrefs); // This will work only when the subtype is not supported. LanguageSwitcherProxy.loadSettings(); if (mSubtypeSwitcher.isKeyboardMode()) { - switcher.loadKeyboard(attribute, mSettingsValues); + switcher.loadKeyboard(editorInfo, mSettingsValues); } if (mSuggestionsView != null) @@ -783,6 +795,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar isSuggestionsStripVisible(), /* needsInputViewShown */ false); // Delay updating suggestions because keyboard input view may not be shown at this point. mHandler.postUpdateSuggestions(); + mHandler.cancelDoubleSpacesTimer(); inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn, mSettingsValues.mKeyPreviewPopupDismissDelay); @@ -793,10 +806,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } - private void initializeInputAttributes(EditorInfo attribute) { - if (attribute == null) + private void initializeInputAttributes(EditorInfo editorInfo) { + if (editorInfo == null) return; - final int inputType = attribute.inputType; + final int inputType = editorInfo.inputType; if (inputType == InputType.TYPE_NULL) { // TODO: We should honor TYPE_NULL specification. Log.i(TAG, "InputType.TYPE_NULL is specified"); @@ -805,7 +818,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final int variation = inputType & InputType.TYPE_MASK_VARIATION; if (inputClass == 0) { Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x imeOptions=0x%08x", - inputType, attribute.imeOptions)); + inputType, editorInfo.imeOptions)); } mInsertSpaceOnPickSuggestionManually = false; @@ -921,6 +934,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart; final boolean candidatesCleared = candidatesStart == -1 && candidatesEnd == -1; if (!mExpectingUpdateSelection) { + if (SPACE_STATE_WEAK == mSpaceState) { + // Test for no WEAK_SPACE action because there is a race condition that may end up + // in coming here on a normal key press. We set this to NONE because after + // a cursor move, we don't want the suggestion strip to swap the space with the + // newly inserted punctuation. + mSpaceState = SPACE_STATE_NONE; + } if (((mComposingStringBuilder.length() > 0 && mHasUncommittedTypedChars) || mVoiceProxy.isVoiceInputHighlighted()) && (selectionChanged || candidatesCleared)) { @@ -938,8 +958,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar TextEntryState.reset(); updateSuggestions(); } - mJustAddedMagicSpace = false; // The user moved the cursor. - mJustReplacedDoubleSpace = false; } mExpectingUpdateSelection = false; mHandler.postUpdateShiftKeyState(); @@ -949,11 +967,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mLastSelectionEnd = newSelEnd; } - public void setLastSelection(int start, int end) { - mLastSelectionStart = start; - mLastSelectionEnd = end; - } - /** * This is called when the user has clicked on the extracted text view, * when running in fullscreen mode. The default implementation hides @@ -1164,25 +1177,22 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return false; } - private void swapSwapperAndSpace() { - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; + // "ic" may be null + private void swapSwapperAndSpaceWhileInBatchEdit(final InputConnection ic) { + if (null == ic) return; CharSequence lastTwo = ic.getTextBeforeCursor(2, 0); // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called. if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Keyboard.CODE_SPACE) { - ic.beginBatchEdit(); ic.deleteSurroundingText(2, 0); ic.commitText(lastTwo.charAt(1) + " ", 1); - ic.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); } } - private void maybeDoubleSpace() { - if (mCorrectionMode == Suggest.CORRECTION_NONE) return; - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; + private boolean maybeDoubleSpaceWhileInBatchEdit(final InputConnection ic) { + if (mCorrectionMode == Suggest.CORRECTION_NONE) return false; + if (ic == null) return false; final CharSequence lastThree = ic.getTextBeforeCursor(3, 0); if (lastThree != null && lastThree.length() == 3 && Utils.canBeFollowedByPeriod(lastThree.charAt(0)) @@ -1190,22 +1200,19 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar && lastThree.charAt(2) == Keyboard.CODE_SPACE && mHandler.isAcceptingDoubleSpaces()) { mHandler.cancelDoubleSpacesTimer(); - ic.beginBatchEdit(); ic.deleteSurroundingText(2, 0); ic.commitText(". ", 1); - ic.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); - mJustReplacedDoubleSpace = true; - } else { - mHandler.startDoubleSpacesTimer(); + return true; } + return false; } - // "ic" must not null - private void maybeRemovePreviousPeriod(final InputConnection ic, CharSequence text) { + // "ic" must not be null + private static 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); + final CharSequence lastOne = ic.getTextBeforeCursor(1, 0); if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_PERIOD && text.charAt(0) == Keyboard.CODE_PERIOD) { @@ -1213,11 +1220,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } - private void removeTrailingSpace() { - final InputConnection ic = getCurrentInputConnection(); + // "ic" may be null + private static void removeTrailingSpaceWhileInBatchEdit(final InputConnection ic) { if (ic == null) return; - - CharSequence lastOne = ic.getTextBeforeCursor(1, 0); + final CharSequence lastOne = ic.getTextBeforeCursor(1, 0); if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_SPACE) { ic.deleteSurroundingText(1, 0); @@ -1233,12 +1239,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return true; } - private boolean isAlphabet(int code) { - if (Character.isLetter(code)) { - return true; - } else { - return false; - } + private static boolean isAlphabet(int code) { + return Character.isLetter(code); } private void onSettingsKeyPressed() { @@ -1254,6 +1256,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Virtual codes representing custom requests. These are used in onCustomRequest() below. public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1; + public static final int CODE_HAPTIC_AND_AUDIO_FEEDBACK = 2; @Override public boolean onCustomRequest(int requestCode) { @@ -1265,6 +1268,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return true; } return false; + case CODE_HAPTIC_AND_AUDIO_FEEDBACK: + hapticAndAudioFeedback(Keyboard.CODE_UNSPECIFIED); + return true; } return false; } @@ -1273,6 +1279,28 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return mOptionsDialog != null && mOptionsDialog.isShowing(); } + private void insertPunctuationFromSuggestionStrip(final InputConnection ic, final int code) { + final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : null; + final int toLeft = TextUtils.isEmpty(beforeText) ? 0 : beforeText.charAt(0); + final boolean shouldRegisterSwapPunctuation; + // If we have a space left of the cursor and it's a weak or a magic space, then we should + // swap it, and override the space state with SPACESTATE_SWAP_PUNCTUATION. + // To swap it, we fool handleSeparator to think the previous space state was a + // magic space. + if (Keyboard.CODE_SPACE == toLeft && mSpaceState == SPACE_STATE_WEAK) { + mSpaceState = SPACE_STATE_MAGIC; + shouldRegisterSwapPunctuation = true; + } else { + shouldRegisterSwapPunctuation = false; + } + onCodeInput(code, new int[] { code }, + KeyboardActionListener.NOT_A_TOUCH_COORDINATE, + KeyboardActionListener.NOT_A_TOUCH_COORDINATE); + if (shouldRegisterSwapPunctuation) { + mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; + } + } + // Implementation of {@link KeyboardActionListener}. @Override public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) { @@ -1283,12 +1311,23 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mLastKeyTime = when; final KeyboardSwitcher switcher = mKeyboardSwitcher; final boolean distinctMultiTouch = switcher.hasDistinctMultitouch(); - final boolean lastStateOfJustReplacedDoubleSpace = mJustReplacedDoubleSpace; - mJustReplacedDoubleSpace = false; + // The space state depends only on the last character pressed and its own previous + // state. Here, we revert the space state to neutral if the key is actually modifying + // the input contents (any non-shift key), which is what we should do for + // all inputs that do not result in a special state. Each character handling is then + // free to override the state as they see fit. + final int spaceState = mSpaceState; + + // TODO: Consolidate the double space timer, mLastKeyTime, and the space state. + if (primaryCode != Keyboard.CODE_SPACE) { + mHandler.cancelDoubleSpacesTimer(); + } + boolean shouldStartKeyTypedTimer = true; switch (primaryCode) { case Keyboard.CODE_DELETE: - handleBackspace(lastStateOfJustReplacedDoubleSpace); + mSpaceState = SPACE_STATE_NONE; + handleBackspace(spaceState); mDeleteCount++; mExpectingUpdateSelection = true; LatinImeLogger.logOnDelete(); @@ -1320,11 +1359,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar break; case Keyboard.CODE_CAPSLOCK: switcher.toggleCapsLock(); - //$FALL-THROUGH$ - case Keyboard.CODE_HAPTIC_AND_AUDIO_FEEDBACK_ONLY: - // Dummy code for haptic and audio feedbacks. - vibrate(); - playKeyClick(primaryCode); + hapticAndAudioFeedback(primaryCode); break; case Keyboard.CODE_SHORTCUT: if (!mHandler.isIgnoringSpecialKey()) { @@ -1344,10 +1379,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // To sum it up: do not update mExpectingUpdateSelection here. break; default: + mSpaceState = SPACE_STATE_NONE; if (mSettingsValues.isWordSeparator(primaryCode)) { - handleSeparator(primaryCode, x, y); + handleSeparator(primaryCode, x, y, spaceState); } else { - handleCharacter(primaryCode, keyCodes, x, y); + handleCharacter(primaryCode, keyCodes, x, y, spaceState); } mExpectingUpdateSelection = true; break; @@ -1372,7 +1408,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar ic.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onKey(Keyboard.CODE_DUMMY); - mJustAddedMagicSpace = false; + mSpaceState = SPACE_STATE_NONE; mEnteredText = text; mHandler.startKeyTypedTimer(); } @@ -1383,7 +1419,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mKeyboardSwitcher.onCancelInput(); } - private void handleBackspace(boolean justReplacedDoubleSpace) { + private void handleBackspace(final int spaceState) { if (mVoiceProxy.logAndRevertVoiceInput()) return; final InputConnection ic = getCurrentInputConnection(); @@ -1421,15 +1457,24 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } mHandler.postUpdateShiftKeyState(); + // TODO: Merge space state with TextEntryState TextEntryState.backspace(); if (TextEntryState.isUndoCommit()) { revertLastWord(ic); ic.endBatchEdit(); return; } - if (justReplacedDoubleSpace) { + if (SPACE_STATE_DOUBLE == spaceState) { if (revertDoubleSpace(ic)) { ic.endBatchEdit(); + // No need to reset mSpaceState, it has already be done (that's why we + // receive it as a parameter) + return; + } + } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) { + if (revertSwapPunctuation(ic)) { + ic.endBatchEdit(); + // Likewise return; } } @@ -1479,18 +1524,24 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } - private void handleCharacter(int primaryCode, int[] keyCodes, int x, int y) { + private void handleCharacter(final int primaryCode, final int[] keyCodes, final int x, + final int y, final int spaceState) { mVoiceProxy.handleCharacter(); - if (mJustAddedMagicSpace && mSettingsValues.isMagicSpaceStripper(primaryCode)) { - removeTrailingSpace(); + final InputConnection ic = getCurrentInputConnection(); + if (ic != null) ic.beginBatchEdit(); + if (SPACE_STATE_MAGIC == spaceState + && mSettingsValues.isMagicSpaceStripper(primaryCode)) { + removeTrailingSpaceWhileInBatchEdit(ic); } int code = primaryCode; if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code)) && isSuggestionsRequested() && !isCursorTouchingWord()) { if (!mHasUncommittedTypedChars) { - mHasUncommittedTypedChars = true; + // Reset entirely the composing state anyway, then start composing a new word unless + // the character is a single quote. + mHasUncommittedTypedChars = (Keyboard.CODE_SINGLE_QUOTE != code); mComposingStringBuilder.setLength(0); mWordComposer.reset(); clearSuggestions(); @@ -1501,6 +1552,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (switcher.isShiftedOrShiftLocked()) { if (keyCodes == null || keyCodes[0] < Character.MIN_CODE_POINT || keyCodes[0] > Character.MAX_CODE_POINT) { + if (null != ic) ic.endBatchEdit(); return; } code = keyCodes[0]; @@ -1514,6 +1566,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { // Some keys, such as [eszett], have upper case as multi-characters. onTextInput(upperCaseString); + if (null != ic) ic.endBatchEdit(); return; } } @@ -1521,7 +1574,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (mHasUncommittedTypedChars) { mComposingStringBuilder.append((char) code); mWordComposer.add(code, keyCodes, x, y); - final InputConnection ic = getCurrentInputConnection(); if (ic != null) { // If it's the first letter, make note of auto-caps state if (mWordComposer.size() == 1) { @@ -1539,18 +1591,19 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { sendKeyChar((char)code); } - if (mJustAddedMagicSpace && mSettingsValues.isMagicSpaceSwapper(primaryCode)) { - swapSwapperAndSpace(); - } else { - mJustAddedMagicSpace = false; + if (SPACE_STATE_MAGIC == spaceState + && mSettingsValues.isMagicSpaceSwapper(primaryCode)) { + if (null != ic) swapSwapperAndSpaceWhileInBatchEdit(ic); } switcher.updateShiftState(); if (LatinIME.PERF_DEBUG) measureCps(); TextEntryState.typedCharacter((char) code, mSettingsValues.isWordSeparator(code), x, y); + if (null != ic) ic.endBatchEdit(); } - private void handleSeparator(int primaryCode, int x, int y) { + private void handleSeparator(final int primaryCode, final int x, final int y, + final int spaceState) { mVoiceProxy.handleSeparator(); mComposingStateManager.onFinishComposingText(); @@ -1580,21 +1633,49 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } - if (mJustAddedMagicSpace) { + final boolean swapMagicSpace; + if (Keyboard.CODE_ENTER == primaryCode && (SPACE_STATE_MAGIC == spaceState + || SPACE_STATE_SWAP_PUNCTUATION == spaceState)) { + removeTrailingSpaceWhileInBatchEdit(ic); + swapMagicSpace = false; + } else if (SPACE_STATE_MAGIC == spaceState) { if (mSettingsValues.isMagicSpaceSwapper(primaryCode)) { - sendKeyChar((char)primaryCode); - swapSwapperAndSpace(); + swapMagicSpace = true; } else { - if (mSettingsValues.isMagicSpaceStripper(primaryCode)) removeTrailingSpace(); - sendKeyChar((char)primaryCode); - mJustAddedMagicSpace = false; + swapMagicSpace = false; + if (mSettingsValues.isMagicSpaceStripper(primaryCode)) { + removeTrailingSpaceWhileInBatchEdit(ic); + } } } else { - sendKeyChar((char)primaryCode); + swapMagicSpace = false; } - if (isSuggestionsRequested() && primaryCode == Keyboard.CODE_SPACE) { - maybeDoubleSpace(); + sendKeyChar((char)primaryCode); + + if (Keyboard.CODE_SPACE == primaryCode) { + if (isSuggestionsRequested()) { + if (maybeDoubleSpaceWhileInBatchEdit(ic)) { + mSpaceState = SPACE_STATE_DOUBLE; + } else if (!isShowingPunctuationList()) { + mSpaceState = SPACE_STATE_WEAK; + } + } + + mHandler.startDoubleSpacesTimer(); + if (!isCursorTouchingWord()) { + mHandler.cancelUpdateSuggestions(); + mHandler.postUpdateBigramPredictions(); + } + } else { + if (swapMagicSpace) { + swapSwapperAndSpaceWhileInBatchEdit(ic); + mSpaceState = SPACE_STATE_MAGIC; + } + + // Set punctuation right away. onUpdateSelection will fire but tests whether it is + // already displayed or not, so it's okay. + setPunctuationSuggestions(); } TextEntryState.typedCharacter((char) primaryCode, true, x, y); @@ -1607,16 +1688,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar ic, mLastSelectionEnd - typedWord.length(), typedWord, mBestWord); } } - if (Keyboard.CODE_SPACE == primaryCode) { - if (!isCursorTouchingWord()) { - mHandler.cancelUpdateSuggestions(); - mHandler.postUpdateBigramPredictions(); - } - } else { - // Set punctuation right away. onUpdateSelection will fire but tests whether it is - // already displayed or not, so it's okay. - setPunctuationSuggestions(); - } mKeyboardSwitcher.updateShiftState(); if (ic != null) { ic.endBatchEdit(); @@ -1651,7 +1722,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public boolean isSuggestionsStripVisible() { if (mSuggestionsView == null) return false; - if (mSuggestionsView.isShowingAddToDictionaryHint() || TextEntryState.isRecorrecting()) + if (mSuggestionsView.isShowingAddToDictionaryHint()) return true; if (!isShowingSuggestionsStrip()) return false; @@ -1749,14 +1820,21 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // a boolean flag. Right now this is handled with a slight hack in // WhitelistDictionary#shouldForciblyAutoCorrectFrom. final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected( - mSuggest.getUnigramDictionaries(), typedWord, preferCapitalization()); + mSuggest.getUnigramDictionaries(), + // If the typed string ends with a single quote, for dictionary lookup purposes + // we behave as if the single quote was not here. Here, we are looking up the + // typed string in the dictionary (to avoid autocorrecting from an existing + // word, so for consistency this lookup should be made WITHOUT the trailing + // single quote. + wordComposer.isLastCharASingleQuote() + ? typedWord.subSequence(0, typedWord.length() - 1) : typedWord, + preferCapitalization()); if (mCorrectionMode == Suggest.CORRECTION_FULL || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) { autoCorrectionAvailable |= (!allowsToBeAutoCorrected); } // Don't auto-correct words with multiple capital letter autoCorrectionAvailable &= !wordComposer.isMostlyCaps(); - autoCorrectionAvailable &= !TextEntryState.isRecorrecting(); // Basically, we update the suggestion strip only when suggestion count > 1. However, // there is an exception: We update the suggestion strip whenever typed word's length @@ -1829,7 +1907,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mVoiceProxy.flushAndLogAllTextModificationCounters(index, suggestion, mSettingsValues.mWordSeparators); - final boolean recorrecting = TextEntryState.isRecorrecting(); final InputConnection ic = getCurrentInputConnection(); if (ic != null) { ic.beginBatchEdit(); @@ -1859,8 +1936,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar LatinImeLogger.logOnManualSuggestion( "", suggestion.toString(), index, suggestions.mWords); // Find out whether the previous character is a space. If it is, as a special case - // 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 + // for punctuation entered through the suggestion strip, it should be swapped + // if it was a magic or a weak space. This is meant to help in case the user // pressed space on purpose of displaying the suggestion strip punctuation. final int rawPrimaryCode = suggestion.charAt(0); // Maybe apply the "bidi mirrored" conversions for parentheses @@ -1868,15 +1945,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final boolean isRtl = keyboard != null && keyboard.mIsRtlKeyboard; final int primaryCode = Key.getRtlParenthesisCode(rawPrimaryCode, isRtl); - final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : ""; - final int toLeft = (ic == null || TextUtils.isEmpty(beforeText)) - ? 0 : beforeText.charAt(0); - final boolean oldMagicSpace = mJustAddedMagicSpace; - if (Keyboard.CODE_SPACE == toLeft) mJustAddedMagicSpace = true; - onCodeInput(primaryCode, new int[] { primaryCode }, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE); - mJustAddedMagicSpace = oldMagicSpace; + insertPunctuationFromSuggestionStrip(ic, primaryCode); + // TODO: the following endBatchEdit seems useless, check if (ic != null) { ic.endBatchEdit(); } @@ -1900,7 +1970,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar suggestion.toString(), index, suggestions.mWords); TextEntryState.acceptedSuggestion(mComposingStringBuilder.toString(), suggestion); // Follow it with a space - if (mInsertSpaceOnPickSuggestionManually && !recorrecting) { + if (mInsertSpaceOnPickSuggestionManually) { sendMagicSpace(); } @@ -1920,13 +1990,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar || !AutoCorrection.isValidWord( mSuggest.getUnigramDictionaries(), suggestion, true)); - if (!recorrecting) { - // Fool the state watcher so that a subsequent backspace will not do a revert, unless - // we just did a correction, in which case we need to stay in - // TextEntryState.State.PICKED_SUGGESTION state. - TextEntryState.typedCharacter((char) Keyboard.CODE_SPACE, true, - WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); - } + // Fool the state watcher so that a subsequent backspace will not do a revert, unless + // we just did a correction, in which case we need to stay in + // TextEntryState.State.PICKED_SUGGESTION state. + TextEntryState.typedCharacter((char) Keyboard.CODE_SPACE, true, + WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); if (!showingAddToDictionaryHint) { // If we're not showing the "Touch again to save", then show corrections again. // In case the cursor position doesn't change, make sure we show the suggestions again. @@ -1936,8 +2004,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // take a noticeable delay to update them which may feel uneasy. } if (showingAddToDictionaryHint) { - if (mIsUserDictionaryAvaliable) { - mSuggestionsView.showAddToDictionaryHint(suggestion); + if (mIsUserDictionaryAvailable) { + mSuggestionsView.showAddToDictionaryHint( + suggestion, mSettingsValues.mHintToSaveText); } else { mHandler.postUpdateSuggestions(); } @@ -2068,13 +2137,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return false; } - // "ic" must not null - private boolean sameAsTextBeforeCursor(final InputConnection ic, CharSequence text) { + // "ic" must not be null + private static boolean sameAsTextBeforeCursor(final InputConnection ic, CharSequence text) { CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0); return TextUtils.equals(text, beforeText); } - // "ic" must not null + // "ic" must not be null private void revertLastWord(final InputConnection ic) { if (mHasUncommittedTypedChars || mComposingStringBuilder.length() <= 0) { sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); @@ -2108,7 +2177,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mHandler.postUpdateSuggestions(); } - // "ic" must not null + // "ic" must not be null private boolean revertDoubleSpace(final InputConnection ic) { mHandler.cancelDoubleSpacesTimer(); // Here we test whether we indeed have a period and a space before us. This should not @@ -2123,13 +2192,28 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return true; } + private static boolean revertSwapPunctuation(final InputConnection ic) { + // Here we test whether we indeed have a space and something else before us. This should not + // be needed, but it's there just in case something went wrong. + final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0); + // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to + // enter surrogate pairs this code will have been removed. + if (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1)) + return false; + ic.beginBatchEdit(); + ic.deleteSurroundingText(2, 0); + ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1); + ic.endBatchEdit(); + return true; + } + public boolean isWordSeparator(int code) { return mSettingsValues.isWordSeparator(code); } private void sendMagicSpace() { sendKeyChar((char)Keyboard.CODE_SPACE); - mJustAddedMagicSpace = true; + mSpaceState = SPACE_STATE_MAGIC; mKeyboardSwitcher.updateShiftState(); } @@ -2155,12 +2239,16 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar loadSettings(); } + private void hapticAndAudioFeedback(int primaryCode) { + vibrate(); + playKeyClick(primaryCode); + } + @Override public void onPress(int primaryCode, boolean withSliding) { final KeyboardSwitcher switcher = mKeyboardSwitcher; if (switcher.isVibrateAndSoundFeedbackRequired()) { - vibrate(); - playKeyClick(primaryCode); + hapticAndAudioFeedback(primaryCode); } final boolean distinctMultiTouch = switcher.hasDistinctMultitouch(); if (distinctMultiTouch && primaryCode == Keyboard.CODE_SHIFT) { diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 773efe709..7d6efa584 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -109,6 +109,7 @@ public class Settings extends InputMethodSettingsActivity public final String mSuggestPuncs; public final SuggestedWords mSuggestPuncList; private final String mSymbolsExcludedFromWordSeparators; + public final CharSequence mHintToSaveText; // From preferences: public final boolean mSoundOn; // Sound setting private to Latin IME (see mSilentModeOn) @@ -158,6 +159,7 @@ public class Settings extends InputMethodSettingsActivity mSuggestPuncs = res.getString(R.string.suggested_punctuations); // TODO: it would be nice not to recreate this each time we change the configuration mSuggestPuncList = createSuggestPuncList(mSuggestPuncs); + mHintToSaveText = context.getText(R.string.hint_add_to_dictionary); // Get the settings preferences final boolean hasVibrator = VibratorCompatWrapper.getInstance(context).hasVibrator(); @@ -299,9 +301,9 @@ public class Settings extends InputMethodSettingsActivity return mShowSettingsKey; } - public boolean isVoiceKeyEnabled(EditorInfo attribute) { + public boolean isVoiceKeyEnabled(EditorInfo editorInfo) { final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); - final int inputType = (attribute != null) ? attribute.inputType : 0; + final int inputType = (editorInfo != null) ? editorInfo.inputType : 0; return shortcutImeEnabled && mVoiceKeyEnabled && !InputTypeCompatUtils.isPasswordInputType(inputType); } @@ -774,4 +776,4 @@ public class Settings extends InputMethodSettingsActivity builder.setView(v); builder.create().show(); } -}
\ No newline at end of file +} diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index caa5aac51..5a3c348a9 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -20,6 +20,7 @@ import android.content.Context; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.ProximityInfo; import java.io.File; @@ -81,6 +82,8 @@ public class Suggest implements Dictionary.WordCallback { public static final String DICT_KEY_USER_BIGRAM = "user_bigram"; public static final String DICT_KEY_WHITELIST ="whitelist"; + private static String SINGLE_QUOTE_AS_STRING = String.valueOf((char)Keyboard.CODE_SINGLE_QUOTE); + private static final boolean DBG = LatinImeLogger.sDBG; private AutoCorrection mAutoCorrection; @@ -101,11 +104,12 @@ public class Suggest implements Dictionary.WordCallback { private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>(); ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>(); - private CharSequence mTypedWord; + private CharSequence mConsideredWord; // TODO: Remove these member variables by passing more context to addWord() callback method private boolean mIsFirstCharCapitalized; private boolean mIsAllUpperCase; + private boolean mIsLastCharASingleQuote; private int mCorrectionMode = CORRECTION_BASIC; @@ -144,7 +148,7 @@ public class Suggest implements Dictionary.WordCallback { initWhitelistAndAutocorrectAndPool(context, locale); } - private void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key, + private static void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key, Dictionary dict) { final Dictionary oldDict = (dict == null) ? dictionaries.remove(key) @@ -295,17 +299,19 @@ public class Suggest implements Dictionary.WordCallback { mAutoCorrection.init(); mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); mIsAllUpperCase = wordComposer.isAllUpperCase(); + mIsLastCharASingleQuote = wordComposer.isLastCharASingleQuote(); collectGarbage(mSuggestions, mPrefMaxSuggestions); Arrays.fill(mScores, 0); - // Save a lowercase version of the original word - String typedWord = wordComposer.getTypedWord(); + final String typedWord = wordComposer.getTypedWord(); + final String consideredWord = mIsLastCharASingleQuote + ? typedWord.substring(0, typedWord.length() - 1) : typedWord; if (typedWord != null) { // Treating USER_TYPED as UNIGRAM suggestion for logging now. LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED, Dictionary.DataType.UNIGRAM); } - mTypedWord = typedWord; + mConsideredWord = consideredWord; if (wordComposer.size() <= 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM || mCorrectionMode == CORRECTION_BASIC)) { @@ -321,7 +327,7 @@ public class Suggest implements Dictionary.WordCallback { for (final Dictionary dictionary : mBigramDictionaries.values()) { dictionary.getBigrams(wordComposer, prevWordForBigram, this); } - if (TextUtils.isEmpty(typedWord)) { + if (TextUtils.isEmpty(consideredWord)) { // Nothing entered: return all bigrams for the previous word int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions); for (int i = 0; i < insertCount; ++i) { @@ -330,7 +336,7 @@ public class Suggest implements Dictionary.WordCallback { } else { // Word entered: return only bigrams that match the first char of the typed word @SuppressWarnings("null") - final char currentChar = typedWord.charAt(0); + final char currentChar = consideredWord.charAt(0); // TODO: Must pay attention to locale when changing case. final char currentCharUpper = Character.toUpperCase(currentChar); int count = 0; @@ -354,24 +360,32 @@ 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, proximityInfo); + if (mIsLastCharASingleQuote) { + final WordComposer tmpWordComposer = new WordComposer(wordComposer); + tmpWordComposer.deleteLast(); + dictionary.getWords(tmpWordComposer, this, proximityInfo); + } else { + dictionary.getWords(wordComposer, this, proximityInfo); + } } } - final String typedWordString = typedWord == null ? null : typedWord.toString(); + final String consideredWordString = + consideredWord == null ? null : consideredWord.toString(); CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase, mIsFirstCharCapitalized, - mWhiteListDictionary.getWhitelistedWord(typedWordString)); + mWhiteListDictionary.getWhitelistedWord(consideredWordString)); mAutoCorrection.updateAutoCorrectionStatus(mUnigramDictionaries, wordComposer, - mSuggestions, mScores, typedWord, mAutoCorrectionThreshold, mCorrectionMode, + mSuggestions, mScores, consideredWord, mAutoCorrectionThreshold, mCorrectionMode, whitelistedWord); if (whitelistedWord != null) { - mSuggestions.add(0, whitelistedWord); + mSuggestions.add(0, mIsLastCharASingleQuote + ? whitelistedWord + SINGLE_QUOTE_AS_STRING : whitelistedWord); } if (typedWord != null) { - mSuggestions.add(0, typedWordString); + mSuggestions.add(0, typedWord.toString()); } Utils.removeDupes(mSuggestions); @@ -424,7 +438,7 @@ public class Suggest implements Dictionary.WordCallback { int pos = 0; // Check if it's the same word, only caps are different - if (Utils.equalsIgnoreCase(mTypedWord, word, offset, length)) { + if (Utils.equalsIgnoreCase(mConsideredWord, word, offset, length)) { // TODO: remove this surrounding if clause and move this logic to // getSuggestedWordBuilder. if (suggestions.size() > 0) { @@ -486,6 +500,9 @@ public class Suggest implements Dictionary.WordCallback { } else { sb.append(word, offset, length); } + if (mIsLastCharASingleQuote) { + sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); + } suggestions.add(pos, sb); if (suggestions.size() > prefMaxSuggestions) { final CharSequence garbage = suggestions.remove(prefMaxSuggestions); @@ -518,7 +535,8 @@ public class Suggest implements Dictionary.WordCallback { return -1; } - private void collectGarbage(ArrayList<CharSequence> suggestions, int prefMaxSuggestions) { + private static void collectGarbage(ArrayList<CharSequence> suggestions, + int prefMaxSuggestions) { int poolSize = StringBuilderPool.getSize(); int garbageSize = suggestions.size(); while (poolSize < prefMaxSuggestions && garbageSize > 0) { diff --git a/java/src/com/android/inputmethod/latin/SuggestionsView.java b/java/src/com/android/inputmethod/latin/SuggestionsView.java index c25ecb382..8c49ba0cf 100644 --- a/java/src/com/android/inputmethod/latin/SuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/SuggestionsView.java @@ -30,7 +30,6 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Message; -import android.os.SystemClock; import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; @@ -172,7 +171,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, public final TextView mWordToSaveView; private final TextView mHintToSaveView; - private final CharSequence mHintToSaveText; public SuggestionsViewParams(Context context, AttributeSet attrs, int defStyle, List<TextView> words, List<View> dividers, List<TextView> infos) { @@ -228,7 +226,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final LayoutInflater inflater = LayoutInflater.from(context); mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null); mHintToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null); - mHintToSaveText = context.getText(R.string.hint_add_to_dictionary); } private static Drawable getMoreSuggestionsHint(Resources res, float textSize, int color) { @@ -445,7 +442,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, } public void layoutAddToDictionaryHint(CharSequence word, ViewGroup stripView, - int stripWidth) { + int stripWidth, CharSequence hintText) { final int width = stripWidth - mDividerWidth - mPadding * 2; final TextView wordView = mWordToSaveView; @@ -464,8 +461,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, final TextView hintView = mHintToSaveView; hintView.setTextColor(mColorAutoCorrect); final int hintWidth = width - wordWidth; - final float hintScaleX = getTextScaleX(mHintToSaveText, hintWidth, hintView.getPaint()); - hintView.setText(mHintToSaveText); + final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint()); + hintView.setText(hintText); hintView.setTextScaleX(hintScaleX); stripView.addView(hintView); setLayoutWeight( @@ -647,9 +644,9 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, && mSuggestionsStrip.getChildAt(0) == mParams.mWordToSaveView; } - public void showAddToDictionaryHint(CharSequence word) { + public void showAddToDictionaryHint(CharSequence word, CharSequence hintText) { clear(); - mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth()); + mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth(), hintText); } public boolean dismissAddToDictionaryHint() { @@ -832,8 +829,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, // Decided to be in the sliding input mode only when the touch point has been moved // upward. mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_SLIDING_MODE; - tracker.onShowMoreKeysPanel( - translatedX, translatedY, SystemClock.uptimeMillis(), moreKeysPanel); + tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel); } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) { // Decided to be in the modal input mode mMoreSuggestionsMode = MORE_SUGGESTIONS_IN_MODAL_MODE; diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java index 79b3bdebb..82242f87e 100644 --- a/java/src/com/android/inputmethod/latin/TextEntryState.java +++ b/java/src/com/android/inputmethod/latin/TextEntryState.java @@ -30,13 +30,10 @@ public class TextEntryState { private static final int IN_WORD = 2; private static final int ACCEPTED_DEFAULT = 3; private static final int PICKED_SUGGESTION = 4; - private static final int PUNCTUATION_AFTER_WORD = 5; - private static final int PUNCTUATION_AFTER_ACCEPTED = 6; - private static final int SPACE_AFTER_ACCEPTED = 7; - private static final int SPACE_AFTER_PICKED = 8; - private static final int UNDO_COMMIT = 9; - private static final int RECORRECTING = 10; - private static final int PICKED_RECORRECTION = 11; + private static final int PUNCTUATION_AFTER_ACCEPTED = 5; + private static final int SPACE_AFTER_ACCEPTED = 6; + private static final int SPACE_AFTER_PICKED = 7; + private static final int UNDO_COMMIT = 8; private static int sState = UNKNOWN; private static int sPreviousState = UNKNOWN; @@ -79,27 +76,11 @@ public class TextEntryState { } public static void acceptedSuggestion(CharSequence typedWord, CharSequence actualWord) { - if (sState == RECORRECTING || sState == PICKED_RECORRECTION) { - setState(PICKED_RECORRECTION); - } else { - setState(PICKED_SUGGESTION); - } + setState(PICKED_SUGGESTION); if (DEBUG) displayState("acceptedSuggestion", "typedWord", typedWord, "actualWord", actualWord); } - public static void selectedForRecorrection() { - setState(RECORRECTING); - if (DEBUG) displayState("selectedForRecorrection"); - } - - public static void onAbortRecorrection() { - if (sState == RECORRECTING || sState == PICKED_RECORRECTION) { - setState(START); - } - if (DEBUG) displayState("onAbortRecorrection"); - } - public static void typedCharacter(char c, boolean isSeparator, int x, int y) { final boolean isSpace = (c == Keyboard.CODE_SPACE); switch (sState) { @@ -123,7 +104,6 @@ public class TextEntryState { } break; case PICKED_SUGGESTION: - case PICKED_RECORRECTION: if (isSpace) { setState(SPACE_AFTER_PICKED); } else if (isSeparator) { @@ -136,7 +116,6 @@ public class TextEntryState { case START: case UNKNOWN: case SPACE_AFTER_ACCEPTED: - case PUNCTUATION_AFTER_WORD: if (!isSpace && !isSeparator) { setState(IN_WORD); } else { @@ -150,9 +129,6 @@ public class TextEntryState { setState(IN_WORD); } break; - case RECORRECTING: - setState(START); - break; } RingCharBuffer.getInstance().push(c, x, y); if (isSeparator) { @@ -178,26 +154,10 @@ public class TextEntryState { if (DEBUG) displayState("reset"); } - public static boolean isAcceptedDefault() { - return sState == ACCEPTED_DEFAULT; - } - - public static boolean isSpaceAfterPicked() { - return sState == SPACE_AFTER_PICKED; - } - public static boolean isUndoCommit() { return sState == UNDO_COMMIT; } - public static boolean isPunctuationAfterAccepted() { - return sState == PUNCTUATION_AFTER_ACCEPTED; - } - - public static boolean isRecorrecting() { - return sState == RECORRECTING || sState == PICKED_RECORRECTION; - } - public static String getState() { return stateName(sState); } @@ -208,13 +168,10 @@ public class TextEntryState { case IN_WORD: return "IN_WORD"; case ACCEPTED_DEFAULT: return "ACCEPTED_DEFAULT"; case PICKED_SUGGESTION: return "PICKED_SUGGESTION"; - case PUNCTUATION_AFTER_WORD: return "PUNCTUATION_AFTER_WORD"; case PUNCTUATION_AFTER_ACCEPTED: return "PUNCTUATION_AFTER_ACCEPTED"; case SPACE_AFTER_ACCEPTED: return "SPACE_AFTER_ACCEPTED"; case SPACE_AFTER_PICKED: return "SPACE_AFTER_PICKED"; case UNDO_COMMIT: return "UNDO_COMMIT"; - case RECORRECTING: return "RECORRECTING"; - case PICKED_RECORRECTION: return "PICKED_RECORRECTION"; default: return "UNKNOWN"; } } diff --git a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java index 9e656675e..3a1af9311 100644 --- a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java @@ -238,7 +238,7 @@ public class UserBigramDictionary extends ExpandableDictionary { /** * Query the database */ - private Cursor query(String selection, String[] selectionArgs) { + private static Cursor query(String selection, String[] selectionArgs) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); // main INNER JOIN frequency ON (main._id=freq.pair_id) @@ -310,7 +310,7 @@ public class UserBigramDictionary extends ExpandableDictionary { } /** Prune any old data if the database is getting too big. */ - private void checkPruneData(SQLiteDatabase db) { + private static void checkPruneData(SQLiteDatabase db) { db.execSQL("PRAGMA foreign_keys = ON;"); Cursor c = db.query(FREQ_TABLE_NAME, new String[] { FREQ_COLUMN_PAIR_ID }, null, null, null, null, null); @@ -380,7 +380,7 @@ public class UserBigramDictionary extends ExpandableDictionary { return null; } - private ContentValues getContentValues(String word1, String word2, String locale) { + private static ContentValues getContentValues(String word1, String word2, String locale) { ContentValues values = new ContentValues(3); values.put(MAIN_COLUMN_WORD1, word1); values.put(MAIN_COLUMN_WORD2, word2); @@ -388,7 +388,7 @@ public class UserBigramDictionary extends ExpandableDictionary { return values; } - private ContentValues getFrequencyContentValues(int pairId, int frequency) { + private static ContentValues getFrequencyContentValues(int pairId, int frequency) { ContentValues values = new ContentValues(2); values.put(FREQ_COLUMN_PAIR_ID, pairId); values.put(FREQ_COLUMN_FREQUENCY, frequency); diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java index 0bbbf3995..3e53bb0a3 100644 --- a/java/src/com/android/inputmethod/latin/UserDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserDictionary.java @@ -134,7 +134,11 @@ public class UserDictionary extends ExpandableDictionary { final Cursor cursor = getContext().getContentResolver() .query(Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null); - addWords(cursor); + try { + addWords(cursor); + } finally { + if (null != cursor) cursor.close(); + } } public boolean isEnabled() { @@ -242,6 +246,5 @@ public class UserDictionary extends ExpandableDictionary { cursor.moveToNext(); } } - cursor.close(); } } diff --git a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java index e41230b3c..de7cb5716 100644 --- a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java @@ -206,7 +206,7 @@ public class UserUnigramDictionary extends ExpandableDictionary { } } - private Cursor query(String selection, String[] selectionArgs) { + private static Cursor query(String selection, String[] selectionArgs) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(USER_UNIGRAM_DICT_TABLE_NAME); qb.setProjectionMap(sDictProjectionMap); @@ -251,7 +251,7 @@ public class UserUnigramDictionary extends ExpandableDictionary { return null; } - private ContentValues getContentValues(String word, int frequency, String locale) { + private static ContentValues getContentValues(String word, int frequency, String locale) { ContentValues values = new ContentValues(4); values.put(COLUMN_WORD, word); values.put(COLUMN_FREQUENCY, frequency); diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index b29ff1975..3d0aa09f1 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -242,7 +242,7 @@ public class Utils { UsabilityStudyLogUtils.getInstance().init(context); return sRingCharBuffer; } - private int normalize(int in) { + private static int normalize(int in) { int ret = in % BUFSIZE; return ret < 0 ? ret + BUFSIZE : ret; } @@ -465,7 +465,7 @@ public class Utils { } } - public void writeBackSpace() { + public static void writeBackSpace() { UsabilityStudyLogUtils.getInstance().write("<backspace>\t0\t0"); } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index adc5637f6..612b16071 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -16,9 +16,11 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyDetector; import java.util.ArrayList; +import java.util.Arrays; /** * A place to store the currently composing word with information such as adjacent key codes as well @@ -41,7 +43,9 @@ public class WordComposer { private int mCapsCount; private boolean mAutoCapitalized; - + // Cache this value for performance + private boolean mIsLastCharASingleQuote; + /** * Whether the user chose to capitalize the first char of the word. */ @@ -53,6 +57,7 @@ public class WordComposer { mTypedWord = new StringBuilder(N); mXCoordinates = new int[N]; mYCoordinates = new int[N]; + mIsLastCharASingleQuote = false; } public WordComposer(WordComposer source) { @@ -62,11 +67,12 @@ public class WordComposer { public void init(WordComposer source) { mCodes = new ArrayList<int[]>(source.mCodes); mTypedWord = new StringBuilder(source.mTypedWord); - mXCoordinates = source.mXCoordinates; - mYCoordinates = source.mYCoordinates; + mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length); + mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length); mCapsCount = source.mCapsCount; mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; mAutoCapitalized = source.mAutoCapitalized; + mIsLastCharASingleQuote = source.mIsLastCharASingleQuote; } /** @@ -77,6 +83,7 @@ public class WordComposer { mTypedWord.setLength(0); mCapsCount = 0; mIsFirstCharCapitalized = false; + mIsLastCharASingleQuote = false; } /** @@ -126,6 +133,7 @@ public class WordComposer { mIsFirstCharCapitalized = isFirstCharCapitalized( newIndex, primaryCode, mIsFirstCharCapitalized); if (Character.isUpperCase(primaryCode)) mCapsCount++; + mIsLastCharASingleQuote = Keyboard.CODE_SINGLE_QUOTE == primaryCode; } /** @@ -135,7 +143,7 @@ public class WordComposer { * @param primaryCode the preferred character * @param codes array of codes based on distance from touch point */ - private void correctPrimaryJuxtapos(int primaryCode, int[] codes) { + private static void correctPrimaryJuxtapos(int primaryCode, int[] codes) { if (codes.length < 2) return; if (codes[0] > 0 && codes[1] > 0 && codes[0] != primaryCode && codes[1] == primaryCode) { codes[1] = codes[0]; @@ -157,6 +165,10 @@ public class WordComposer { } if (size() == 0) { mIsFirstCharCapitalized = false; + mIsLastCharASingleQuote = false; + } else { + mIsLastCharASingleQuote = + Keyboard.CODE_SINGLE_QUOTE == mTypedWord.codePointAt(mTypedWord.length() - 1); } } @@ -179,6 +191,10 @@ public class WordComposer { return mIsFirstCharCapitalized; } + public boolean isLastCharASingleQuote() { + return mIsLastCharASingleQuote; + } + /** * Whether or not all of the user typed chars are upper case * @return true if all user typed chars are upper case, false otherwise diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 095c2c51c..93540ab12 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -82,15 +82,15 @@ public class AndroidSpellCheckerService extends SpellCheckerService { // The threshold for a candidate to be offered as a suggestion. private double mSuggestionThreshold; - // The threshold for a suggestion to be considered "likely". - private double mLikelyThreshold; + // The threshold for a suggestion to be considered "recommended". + private double mRecommendedThreshold; @Override public void onCreate() { super.onCreate(); mSuggestionThreshold = Double.parseDouble(getString(R.string.spellchecker_suggestion_threshold_value)); - mLikelyThreshold = - Double.parseDouble(getString(R.string.spellchecker_likely_threshold_value)); + mRecommendedThreshold = + Double.parseDouble(getString(R.string.spellchecker_recommended_threshold_value)); } @Override @@ -110,10 +110,11 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private static class SuggestionsGatherer implements WordCallback { public static class Result { public final String[] mSuggestions; - public final boolean mHasLikelySuggestions; - public Result(final String[] gatheredSuggestions, final boolean hasLikelySuggestions) { + public final boolean mHasRecommendedSuggestions; + public Result(final String[] gatheredSuggestions, + final boolean hasRecommendedSuggestions) { mSuggestions = gatheredSuggestions; - mHasLikelySuggestions = hasLikelySuggestions; + mHasRecommendedSuggestions = hasRecommendedSuggestions; } } @@ -121,7 +122,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private final int[] mScores; private final String mOriginalText; private final double mSuggestionThreshold; - private final double mLikelyThreshold; + private final double mRecommendedThreshold; private final int mMaxLength; private int mLength = 0; @@ -131,10 +132,10 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private int mBestScore = Integer.MIN_VALUE; // As small as possible SuggestionsGatherer(final String originalText, final double suggestionThreshold, - final double likelyThreshold, final int maxLength) { + final double recommendedThreshold, final int maxLength) { mOriginalText = originalText; mSuggestionThreshold = suggestionThreshold; - mLikelyThreshold = likelyThreshold; + mRecommendedThreshold = recommendedThreshold; mMaxLength = maxLength; mSuggestions = new ArrayList<CharSequence>(maxLength + 1); mScores = new int[mMaxLength]; @@ -198,19 +199,19 @@ public class AndroidSpellCheckerService extends SpellCheckerService { public Result getResults(final int capitalizeType, final Locale locale) { final String[] gatheredSuggestions; - final boolean hasLikelySuggestions; + final boolean hasRecommendedSuggestions; if (0 == mLength) { // Either we found no suggestions, or we found some BUT the max length was 0. // If we found some mBestSuggestion will not be null. If it is null, then // we found none, regardless of the max length. if (null == mBestSuggestion) { gatheredSuggestions = null; - hasLikelySuggestions = false; + hasRecommendedSuggestions = false; } else { gatheredSuggestions = EMPTY_STRING_ARRAY; final double normalizedScore = Utils.calcNormalizedScore(mOriginalText, mBestSuggestion, mBestScore); - hasLikelySuggestions = (normalizedScore > mLikelyThreshold); + hasRecommendedSuggestions = (normalizedScore > mRecommendedThreshold); } } else { if (DBG) { @@ -244,15 +245,15 @@ public class AndroidSpellCheckerService extends SpellCheckerService { final CharSequence bestSuggestion = mSuggestions.get(0); final double normalizedScore = Utils.calcNormalizedScore(mOriginalText, bestSuggestion, bestScore); - hasLikelySuggestions = (normalizedScore > mLikelyThreshold); + hasRecommendedSuggestions = (normalizedScore > mRecommendedThreshold); if (DBG) { Log.i(TAG, "Best suggestion : " + bestSuggestion + ", score " + bestScore); Log.i(TAG, "Normalized score = " + normalizedScore - + " (threshold " + mLikelyThreshold - + ") => hasLikelySuggestions = " + hasLikelySuggestions); + + " (threshold " + mRecommendedThreshold + + ") => hasRecommendedSuggestions = " + hasRecommendedSuggestions); } } - return new Result(gatheredSuggestions, hasLikelySuggestions); + return new Result(gatheredSuggestions, hasRecommendedSuggestions); } } @@ -360,6 +361,27 @@ public class AndroidSpellCheckerService extends SpellCheckerService { mLocale = LocaleUtils.constructLocaleFromString(localeString); } + /* + * Returns whether the code point is a letter that makes sense for the specified + * locale for this spell checker. + * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml + * and is limited to EFIGS language. + * Hence at the moment this explicitly excludes non-Latin scripts, including CJK + * characters, but also Cyrillic, Arabic or Hebrew characters. + * The locale should be used to rule out inappropriate characters when we support + * spellchecking other languages like Russian. + */ + private static boolean isLetterCheckableByLanguage(final int codePoint, + final Locale locale) { + // Our supported dictionaries (EFIGS) at the moment only includes characters + // in the C0, C1, Latin Extended A and B, IPA extensions unicode blocks. + // As it happens, those are back-to-back in the code range 0x40 to 0x2AF, so + // the below is a very efficient way to test for it. As for the 0-0x3F, it's + // excluded from isLetter anyway. + // TODO: change this to use locale when we support other scripts + return codePoint <= 0x2AF && Character.isLetter(codePoint); + } + /** * Finds out whether a particular string should be filtered out of spell checking. * @@ -368,7 +390,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { * @param text the string to evaluate. * @return true if we should filter this text out, false otherwise */ - private boolean shouldFilterOut(final String text) { + private static boolean shouldFilterOut(final String text, final Locale locale) { if (TextUtils.isEmpty(text) || text.length() <= 1) return true; // TODO: check if an equivalent processing can't be done more quickly with a @@ -376,7 +398,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { // Filter by first letter final int firstCodePoint = text.codePointAt(0); // Filter out words that don't start with a letter or an apostrophe - if (!Character.isLetter(firstCodePoint) + if (!isLetterCheckableByLanguage(firstCodePoint, locale) && '\'' != firstCodePoint) return true; // Filter contents @@ -389,7 +411,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { // words or a URI - in either case we don't want to spell check that if ('@' == codePoint || '/' == codePoint) return true; - if (Character.isLetter(codePoint)) ++letterCount; + if (isLetterCheckableByLanguage(codePoint, locale)) ++letterCount; } // Guestimate heuristic: perform spell checking if at least 3/4 of the characters // in this word are letters @@ -408,7 +430,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { try { final String text = textInfo.getText(); - if (shouldFilterOut(text)) { + if (shouldFilterOut(text, mLocale)) { DictAndProximity dictInfo = null; try { dictInfo = mDictionaryPool.takeOrGetNull(); @@ -426,7 +448,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService { // TODO: Don't gather suggestions if the limit is <= 0 unless necessary final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(text, - mService.mSuggestionThreshold, mService.mLikelyThreshold, suggestionsLimit); + mService.mSuggestionThreshold, mService.mRecommendedThreshold, + suggestionsLimit); final WordComposer composer = new WordComposer(); final int length = text.length(); for (int i = 0; i < length; ++i) { @@ -475,7 +498,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { + suggestionsLimit); Log.i(TAG, "IsInDict = " + isInDict); Log.i(TAG, "LooksLikeTypo = " + (!isInDict)); - Log.i(TAG, "HasLikelySuggestions = " + result.mHasLikelySuggestions); + Log.i(TAG, "HasRecommendedSuggestions = " + result.mHasRecommendedSuggestions); if (null != result.mSuggestions) { for (String suggestion : result.mSuggestions) { Log.i(TAG, suggestion); @@ -483,10 +506,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService { } } - // TODO: actually use result.mHasLikelySuggestions final int flags = (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY - : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO); + : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) + | (result.mHasRecommendedSuggestions + ? SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS + : 0); return new SuggestionsInfo(flags, result.mSuggestions); } catch (RuntimeException e) { // Don't kill the keyboard if there is a bug in the spell checker |