diff options
Diffstat (limited to 'java/src')
19 files changed, 336 insertions, 261 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/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 7302830d4..e01262c20 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -136,16 +136,14 @@ public class KeyCodeDescriptionMapper { if (!TextUtils.isEmpty(key.mLabel)) { final String label = key.mLabel.toString().trim(); + // First, attempt to map the label to a pre-defined description. if (mKeyLabelMap.containsKey(label)) { return context.getString(mKeyLabelMap.get(label)); - } else if (label.length() == 1 - || (keyboard.isManualTemporaryUpperCase() && !TextUtils - .isEmpty(key.mHintLabel))) { - return getDescriptionForKeyCode(context, keyboard, key, shouldObscure); - } else { - return label; } - } else if (key.mCode != Keyboard.CODE_DUMMY) { + } + + // Just attempt to speak the description. + if (key.mCode != Keyboard.CODE_DUMMY) { return getDescriptionForKeyCode(context, keyboard, key, shouldObscure); } @@ -187,11 +185,14 @@ public class KeyCodeDescriptionMapper { * @return the key code for the specified key */ private int getCorrectKeyCode(Keyboard keyboard, Key key) { - if (keyboard.isManualTemporaryUpperCase() && !TextUtils.isEmpty(key.mHintLabel)) { + // If keyboard is in manual temporary upper case state and key has + // manual temporary uppercase letter as key hint letter, alternate + // character code should be sent. + if (keyboard.isManualTemporaryUpperCase() && key.hasUppercaseLetter() + && !TextUtils.isEmpty(key.mHintLabel)) { return key.mHintLabel.charAt(0); - } else { - return key.mCode; } + return key.mCode; } /** 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 f2014b771..ce5f29d73 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,10 @@ 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; + private static final int ACTION_FLAGS_IGNORE_WHILE_TYPING = 0x04; /** The current pressed state of this key */ private boolean mPressed; @@ -181,9 +183,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; @@ -254,8 +256,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 +276,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 +318,23 @@ 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 boolean ignoreWhileTyping() { + return (mActionFlags & ACTION_FLAGS_IGNORE_WHILE_TYPING) != 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 +343,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 +356,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 +380,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 8d40e7aa5..4a28bf1de 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -169,6 +169,10 @@ public class Keyboard { return mShiftState.isShiftLocked(); } + public boolean isShiftLockShifted() { + return mShiftState.isShiftLockShifted(); + } + public boolean setShifted(boolean newShiftState) { for (final Key key : mShiftKeys) { if (!newShiftState && !mShiftState.isShiftLocked()) { @@ -213,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 139e5eddf..4d30077e3 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -386,6 +386,13 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha return false; } + private boolean isShiftLockShifted() { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null) + return latinKeyboard.isShiftLockShifted(); + return false; + } + public boolean isAutomaticTemporaryUpperCase() { LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard != null) @@ -559,6 +566,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha if (shiftKeyState.isMomentary()) { // After chording input while normal state. toggleShift(); + } else if (isShiftLocked() && !isShiftLockShifted() && shiftKeyState.isPressing() + && !withSliding) { + // Shift has been long pressed, ignore this release. } else if (isShiftLocked() && !shiftKeyState.isIgnoring() && !withSliding) { // Shift has been pressed without chording while caps lock state. toggleCapsLock(); diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 9d97cbf6c..60631e867 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -89,6 +89,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke private static final int MSG_REPEAT_KEY = 1; private static final int MSG_LONGPRESS_KEY = 2; private static final int MSG_IGNORE_DOUBLE_TAP = 3; + private static final int MSG_KEY_TYPED = 4; private boolean mInKeyRepeat; @@ -138,6 +139,17 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke } @Override + public void startKeyTypedTimer(long delay) { + removeMessages(MSG_KEY_TYPED); + sendMessageDelayed(obtainMessage(MSG_KEY_TYPED), delay); + } + + @Override + public boolean isTyping() { + return hasMessages(MSG_KEY_TYPED); + } + + @Override public void cancelKeyTimers() { cancelKeyRepeatTimer(); cancelLongPressTimer(); @@ -263,20 +275,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. @@ -400,18 +398,22 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke if (primaryCode == Keyboard.CODE_DIGIT0 && latinKeyboard.isPhoneKeyboard()) { tracker.onLongPressed(); // Long pressing on 0 in phone number keypad gives you a '+'. - return invokeOnKey(Keyboard.CODE_PLUS); + invokeCodeInput(Keyboard.CODE_PLUS); + invokeReleaseKey(primaryCode); + return true; } if (primaryCode == Keyboard.CODE_SHIFT && latinKeyboard.isAlphaKeyboard()) { tracker.onLongPressed(); - return invokeOnKey(Keyboard.CODE_CAPSLOCK); + invokeCodeInput(Keyboard.CODE_CAPSLOCK); + invokeReleaseKey(primaryCode); + return true; } } if (primaryCode == Keyboard.CODE_SETTINGS || primaryCode == Keyboard.CODE_SPACE) { // Both long pressing settings key and space key invoke IME switcher dialog. - if (getKeyboardActionListener().onCustomRequest( - LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { + if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { tracker.onLongPressed(); + invokeReleaseKey(primaryCode); return true; } else { return openMoreKeysPanel(parentKey, tracker); @@ -421,11 +423,18 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke } } - private boolean invokeOnKey(int primaryCode) { + private boolean invokeCustomRequest(int code) { + return getKeyboardActionListener().onCustomRequest(code); + } + + private void invokeCodeInput(int primaryCode) { getKeyboardActionListener().onCodeInput(primaryCode, null, KeyboardActionListener.NOT_A_TOUCH_COORDINATE, KeyboardActionListener.NOT_A_TOUCH_COORDINATE); - return true; + } + + private void invokeReleaseKey(int primaryCode) { + getKeyboardActionListener().onRelease(primaryCode, false); } private boolean openMoreKeysPanel(Key parentKey, PointerTracker tracker) { diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index d5986aa32..f7a0d97ae 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -74,6 +74,8 @@ public class PointerTracker { } public interface TimerProxy { + public void startKeyTypedTimer(long delay); + public boolean isTyping(); public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker); public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker); public void cancelLongPressTimer(); @@ -81,6 +83,10 @@ public class PointerTracker { public static class Adapter implements TimerProxy { @Override + public void startKeyTypedTimer(long delay) {} + @Override + public boolean isTyping() { return false; } + @Override public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {} @Override public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {} @@ -98,6 +104,7 @@ public class PointerTracker { private static int sLongPressKeyTimeout; private static int sLongPressShiftKeyTimeout; private static int sLongPressSpaceKeyTimeout; + private static int sIgnoreSpecialKeyTimeout; private static int sTouchNoiseThresholdMillis; private static int sTouchNoiseThresholdDistanceSquared; @@ -168,7 +175,9 @@ public class PointerTracker { sLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout); sLongPressShiftKeyTimeout = res.getInteger(R.integer.config_long_press_shift_key_timeout); sLongPressSpaceKeyTimeout = res.getInteger(R.integer.config_long_press_space_key_timeout); + sIgnoreSpecialKeyTimeout = res.getInteger(R.integer.config_ignore_special_key_timeout); sTouchNoiseThresholdMillis = res.getInteger(R.integer.config_touch_noise_threshold_millis); + final float touchNoiseThresholdDistance = res.getDimension( R.dimen.config_touch_noise_threshold_distance); sTouchNoiseThresholdDistanceSquared = (int)( @@ -233,8 +242,9 @@ public class PointerTracker { if (DEBUG_LISTENER) Log.d(TAG, "onPress : " + keyCodePrintable(key.mCode) + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey); - if (ignoreModifierKey) + if (ignoreModifierKey) { return false; + } if (key.isEnabled()) { mListener.onPress(key.mCode, withSliding); final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; @@ -254,15 +264,17 @@ public class PointerTracker { + " ignoreModifier=" + ignoreModifierKey); if (ignoreModifierKey) return; - if (key.isEnabled()) + if (key.isEnabled()) { mListener.onCodeInput(primaryCode, keyCodes, x, y); + } } private void callListenerOnTextInput(Key key) { if (DEBUG_LISTENER) Log.d(TAG, "onTextInput: text=" + key.mOutputText); - if (key.isEnabled()) + if (key.isEnabled()) { mListener.onTextInput(key.mOutputText); + } } // Note that we need primaryCode argument because the keyboard may in shifted state and the @@ -274,8 +286,9 @@ public class PointerTracker { + withSliding + " ignoreModifier=" + ignoreModifierKey); if (ignoreModifierKey) return; - if (key.isEnabled()) + if (key.isEnabled()) { mListener.onRelease(primaryCode, withSliding); + } } private void callListenerOnCancelInput() { @@ -343,7 +356,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(); @@ -351,15 +364,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; - if (isModifierCode(code) || code == Keyboard.CODE_DELETE - || code == Keyboard.CODE_ENTER || code == Keyboard.CODE_SPACE) - return false; - return true; - } - public int getLastX() { return mLastX; } @@ -656,7 +660,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; @@ -709,15 +713,21 @@ public class PointerTracker { } } - private void detectAndSendKey(int index, int x, int y) { - final Key key = getKey(index); + private void detectAndSendKey(int keyIndex, int x, int y) { + final Key key = getKey(keyIndex); if (key == null) { callListenerOnCancelInput(); return; } if (key.mOutputText != null) { - callListenerOnTextInput(key); + final boolean ignoreText = key.ignoreWhileTyping() && mTimerProxy.isTyping(); + if (!ignoreText) { + callListenerOnTextInput(key); + } callListenerOnRelease(key, key.mCode, false); + if (!ignoreText) { + mTimerProxy.startKeyTypedTimer(sIgnoreSpecialKeyTimeout); + } } else { int code = key.mCode; final int[] codes = mKeyDetector.newCodeArray(); @@ -737,8 +747,16 @@ public class PointerTracker { codes[1] = codes[0]; codes[0] = code; } - callListenerOnCodeInput(key, code, codes, x, y); + final boolean ignoreCode = key.ignoreWhileTyping() && mTimerProxy.isTyping(); + if (!ignoreCode) { + // TODO: It might be useful to register the nearest key code in codes[] instead of + // just ignoring. + callListenerOnCodeInput(key, code, codes, x, y); + } callListenerOnRelease(key, code, false); + if (!key.ignoreWhileTyping() && !isModifierCode(code)) { + mTimerProxy.startKeyTypedTimer(sIgnoreSpecialKeyTimeout); + } } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 39fb521ea..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) diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 2d8b7bf11..faa5f86f2 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -42,10 +42,8 @@ public class KeyboardIconsSet { private static final int ICON_SHIFTED_SHIFT_KEY = 10; // This should be aligned with Keyboard.keyIconPreview enum. private static final int ICON_PREVIEW_TAB_KEY = 11; - private static final int ICON_PREVIEW_SETTINGS_KEY = 12; - private static final int ICON_PREVIEW_SHORTCUT_KEY = 13; - private static final int ICON_LAST = 13; + private static final int ICON_LAST = 11; private final Drawable mIcons[] = new Drawable[ICON_LAST + 1]; @@ -73,10 +71,6 @@ public class KeyboardIconsSet { return ICON_SHIFTED_SHIFT_KEY; case R.styleable.Keyboard_iconPreviewTabKey: return ICON_PREVIEW_TAB_KEY; - case R.styleable.Keyboard_iconPreviewSettingsKey: - return ICON_PREVIEW_SETTINGS_KEY; - case R.styleable.Keyboard_iconPreviewShortcutKey: - return ICON_PREVIEW_SHORTCUT_KEY; default: return ICON_UNDEFINED; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java index fd98456a8..28a53cedc 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardShiftState.java @@ -103,6 +103,10 @@ public class KeyboardShiftState { return mState == SHIFT_LOCKED || mState == SHIFT_LOCK_SHIFTED; } + public boolean isShiftLockShifted() { + return mState == SHIFT_LOCK_SHIFTED; + } + public boolean isAutomaticTemporaryUpperCase() { return mState == AUTO_SHIFTED; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java index 55175e046..08e7a7a4e 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java @@ -21,13 +21,13 @@ import com.android.inputmethod.keyboard.PointerTracker; import java.util.LinkedList; public class PointerTrackerQueue { - private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>(); + private final LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>(); - public void add(PointerTracker tracker) { + public synchronized void add(PointerTracker tracker) { mQueue.add(tracker); } - public void releaseAllPointersOlderThan(PointerTracker tracker, long eventTime) { + public synchronized void releaseAllPointersOlderThan(PointerTracker tracker, long eventTime) { if (mQueue.lastIndexOf(tracker) < 0) { return; } @@ -47,25 +47,28 @@ public class PointerTrackerQueue { releaseAllPointersExcept(null, eventTime); } - public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) { + public synchronized void releaseAllPointersExcept(PointerTracker tracker, long eventTime) { for (PointerTracker t : mQueue) { - if (t == tracker) + if (t == tracker) { continue; + } t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime); } mQueue.clear(); - if (tracker != null) + if (tracker != null) { mQueue.add(tracker); + } } - public void remove(PointerTracker tracker) { + public synchronized void remove(PointerTracker tracker) { mQueue.remove(tracker); } - public boolean isAnyInSlidingKeyInput() { + public synchronized boolean isAnyInSlidingKeyInput() { for (final PointerTracker tracker : mQueue) { - if (tracker.isInSlidingKeyInput()) + if (tracker.isInSlidingKeyInput()) { return true; + } } return false; } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 167e500f6..f7a77cae7 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -193,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}. @@ -254,10 +254,33 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private static final int MSG_SET_BIGRAM_PREDICTIONS = 6; private static final int MSG_PENDING_IMS_CALLBACK = 7; + private int mDelayBeforeFadeoutLanguageOnSpacebar; + private int mDelayUpdateSuggestions; + private int mDelayUpdateShiftState; + private int mDurationOfFadeoutLanguageOnSpacebar; + private float mFinalFadeoutFactorOfLanguageOnSpacebar; + private long mDoubleSpacesTurnIntoPeriodTimeout; + public UIHandler(LatinIME outerInstance) { super(outerInstance); } + public void onCreate() { + final Resources res = getOuterInstance().getResources(); + mDelayBeforeFadeoutLanguageOnSpacebar = res.getInteger( + R.integer.config_delay_before_fadeout_language_on_spacebar); + mDelayUpdateSuggestions = + res.getInteger(R.integer.config_delay_update_suggestions); + mDelayUpdateShiftState = + res.getInteger(R.integer.config_delay_update_shift_state); + mDurationOfFadeoutLanguageOnSpacebar = res.getInteger( + R.integer.config_duration_of_fadeout_language_on_spacebar); + mFinalFadeoutFactorOfLanguageOnSpacebar = res.getInteger( + R.integer.config_final_fadeout_percentage_of_language_on_spacebar) / 100.0f; + mDoubleSpacesTurnIntoPeriodTimeout = res.getInteger( + R.integer.config_double_spaces_turn_into_period_timeout); + } + @Override public void handleMessage(Message msg) { final LatinIME latinIme = getOuterInstance(); @@ -280,17 +303,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR: if (inputView != null) { inputView.setSpacebarTextFadeFactor( - (1.0f + latinIme.mSettingsValues. - mFinalFadeoutFactorOfLanguageOnSpacebar) / 2, + (1.0f + mFinalFadeoutFactorOfLanguageOnSpacebar) / 2, (LatinKeyboard)msg.obj); } sendMessageDelayed(obtainMessage(MSG_DISMISS_LANGUAGE_ON_SPACEBAR, msg.obj), - latinIme.mSettingsValues.mDurationOfFadeoutLanguageOnSpacebar); + mDurationOfFadeoutLanguageOnSpacebar); break; case MSG_DISMISS_LANGUAGE_ON_SPACEBAR: if (inputView != null) { - inputView.setSpacebarTextFadeFactor( - latinIme.mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar, + inputView.setSpacebarTextFadeFactor(mFinalFadeoutFactorOfLanguageOnSpacebar, (LatinKeyboard)msg.obj); } break; @@ -299,8 +320,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void postUpdateSuggestions() { removeMessages(MSG_UPDATE_SUGGESTIONS); - sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), - getOuterInstance().mSettingsValues.mDelayUpdateSuggestions); + sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), mDelayUpdateSuggestions); } public void cancelUpdateSuggestions() { @@ -313,8 +333,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void postUpdateShiftKeyState() { removeMessages(MSG_UPDATE_SHIFT_STATE); - sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), - getOuterInstance().mSettingsValues.mDelayUpdateShiftState); + sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), mDelayUpdateShiftState); } public void cancelUpdateShiftState() { @@ -323,8 +342,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void postUpdateBigramPredictions() { removeMessages(MSG_SET_BIGRAM_PREDICTIONS); - sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS), - getOuterInstance().mSettingsValues.mDelayUpdateSuggestions); + sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS), mDelayUpdateSuggestions); } public void cancelUpdateBigramPredictions() { @@ -344,26 +362,24 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final LatinKeyboard keyboard = latinIme.mKeyboardSwitcher.getLatinKeyboard(); // The language is always displayed when the delay is negative. final boolean needsToDisplayLanguage = localeChanged - || latinIme.mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar < 0; + || mDelayBeforeFadeoutLanguageOnSpacebar < 0; // The language is never displayed when the delay is zero. - if (latinIme.mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar != 0) { + if (mDelayBeforeFadeoutLanguageOnSpacebar != 0) { inputView.setSpacebarTextFadeFactor(needsToDisplayLanguage ? 1.0f - : latinIme.mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar, + : mFinalFadeoutFactorOfLanguageOnSpacebar, keyboard); } // The fadeout animation will start when the delay is positive. - if (localeChanged - && latinIme.mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar > 0) { + if (localeChanged && mDelayBeforeFadeoutLanguageOnSpacebar > 0) { sendMessageDelayed(obtainMessage(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR, keyboard), - latinIme.mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar); + mDelayBeforeFadeoutLanguageOnSpacebar); } } } public void startDoubleSpacesTimer() { removeMessages(MSG_SPACE_TYPED); - sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), - getOuterInstance().mSettingsValues.mDoubleSpacesTurnIntoPeriodTimeout); + sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), mDoubleSpacesTurnIntoPeriodTimeout); } public void cancelDoubleSpacesTimer() { @@ -380,6 +396,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); @@ -395,18 +412,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; @@ -417,27 +434,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) { @@ -447,6 +465,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { final LatinIME latinIme = getOuterInstance(); latinIme.onFinishInputViewInternal(finishingInput); + mAppliedEditorInfo = null; } } @@ -480,6 +499,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); mVibrator = VibratorCompatWrapper.getInstance(this); + mHandler.onCreate(); DEBUG = LatinImeLogger.sDBG; final Resources res = getResources(); @@ -552,7 +572,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mUserDictionary = new UserDictionary(this, localeStr); mSuggest.setUserDictionary(mUserDictionary); - mIsUserDictionaryAvaliable = mUserDictionary.isEnabled(); + mIsUserDictionaryAvailable = mUserDictionary.isEnabled(); resetContactsDictionary(oldContactsDictionary); @@ -673,13 +693,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 @@ -692,19 +712,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) { @@ -714,7 +734,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(); @@ -725,14 +745,14 @@ 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; @@ -748,12 +768,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) @@ -773,10 +793,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"); @@ -785,7 +805,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; @@ -934,11 +954,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 @@ -1276,12 +1291,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Implementation of {@link KeyboardActionListener}. @Override public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) { - long when = SystemClock.uptimeMillis(); + final long when = SystemClock.uptimeMillis(); if (primaryCode != Keyboard.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) { mDeleteCount = 0; } mLastKeyTime = when; - KeyboardSwitcher switcher = mKeyboardSwitcher; + final KeyboardSwitcher switcher = mKeyboardSwitcher; final boolean distinctMultiTouch = switcher.hasDistinctMultitouch(); // 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 @@ -1305,13 +1320,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar break; case Keyboard.CODE_SHIFT: // Shift key is handled in onPress() when device has distinct multi-touch panel. - if (!distinctMultiTouch) + if (!distinctMultiTouch) { switcher.toggleShift(); + } break; case Keyboard.CODE_SWITCH_ALPHA_SYMBOL: // Symbol key is handled in onPress() when device has distinct multi-touch panel. - if (!distinctMultiTouch) + if (!distinctMultiTouch) { switcher.changeKeyboardMode(); + } break; case Keyboard.CODE_CANCEL: if (!isShowingOptionDialog()) { @@ -1496,7 +1513,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar 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(); @@ -1775,7 +1794,15 @@ 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); @@ -1951,7 +1978,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // take a noticeable delay to update them which may feel uneasy. } if (showingAddToDictionaryHint) { - if (mIsUserDictionaryAvaliable) { + if (mIsUserDictionaryAvailable) { mSuggestionsView.showAddToDictionaryHint( suggestion, mSettingsValues.mHintToSaveText); } else { diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 9d5d89071..7d6efa584 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -102,13 +102,7 @@ public class Settings extends InputMethodSettingsActivity public static class Values { // From resources: - public final int mDelayBeforeFadeoutLanguageOnSpacebar; - public final int mDelayUpdateSuggestions; public final int mDelayUpdateOldSuggestions; - public final int mDelayUpdateShiftState; - public final int mDurationOfFadeoutLanguageOnSpacebar; - public final float mFinalFadeoutFactorOfLanguageOnSpacebar; - public final long mDoubleSpacesTurnIntoPeriodTimeout; public final String mWordSeparators; public final String mMagicSpaceStrippers; public final String mMagicSpaceSwappers; @@ -148,20 +142,8 @@ public class Settings extends InputMethodSettingsActivity } // Get the resources - mDelayBeforeFadeoutLanguageOnSpacebar = res.getInteger( - R.integer.config_delay_before_fadeout_language_on_spacebar); - mDelayUpdateSuggestions = - res.getInteger(R.integer.config_delay_update_suggestions); mDelayUpdateOldSuggestions = res.getInteger( R.integer.config_delay_update_old_suggestions); - mDelayUpdateShiftState = - res.getInteger(R.integer.config_delay_update_shift_state); - mDurationOfFadeoutLanguageOnSpacebar = res.getInteger( - R.integer.config_duration_of_fadeout_language_on_spacebar); - mFinalFadeoutFactorOfLanguageOnSpacebar = res.getInteger( - R.integer.config_final_fadeout_percentage_of_language_on_spacebar) / 100.0f; - mDoubleSpacesTurnIntoPeriodTimeout = res.getInteger( - R.integer.config_double_spaces_turn_into_period_timeout); mMagicSpaceStrippers = res.getString(R.string.magic_space_stripping_symbols); mMagicSpaceSwappers = res.getString(R.string.magic_space_swapping_symbols); String wordSeparators = mMagicSpaceStrippers + mMagicSpaceSwappers @@ -319,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); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 97e91745c..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; @@ -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); 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/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 7f3a54244..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; } /** @@ -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 |