diff options
35 files changed, 1963 insertions, 954 deletions
diff --git a/java/res/values-ar/donottranslate-more-keys.xml b/java/res/values-ar/donottranslate-more-keys.xml index cde686084..e49677adb 100644 --- a/java/res/values-ar/donottranslate-more-keys.xml +++ b/java/res/values-ar/donottranslate-more-keys.xml @@ -70,10 +70,8 @@ <string name="keylabel_for_symbols_percent">\u066a</string> <string name="more_keys_for_comma">,</string> <string name="more_keys_for_f1">,</string> - <!-- @icon/3 is iconSettingsKey --> - <string name="more_keys_for_f1_settings">\\,,\@icon/3|\@integer/key_settings</string> - <!-- @icon/7 is iconTabKey --> - <string name="more_keys_for_f1_navigate">\\,,\@icon/7|\@integer/key_tab</string> + <string name="more_keys_for_f1_settings">\\,,\@icon/settingsKey|\@integer/key_settings</string> + <string name="more_keys_for_f1_navigate">\\,,\@icon/tabKey|\@integer/key_tab</string> <string name="more_keys_for_symbols_question">\?</string> <string name="more_keys_for_symbols_semicolon">;</string> <string name="more_keys_for_symbols_percent">%,‰</string> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 9892d7835..57930c675 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -248,12 +248,10 @@ <!-- This should be aligned with the KeyboardIcons.ICONS_TO_ATTRS_MAP --> <enum name="iconShiftKey" value="1" /> <enum name="iconDeleteKey" value="2" /> - <!-- This is also represented as "@icon/3" in keyboard layout XML. --> <enum name="iconSettingsKey" value="3" /> <enum name="iconSpaceKey" value="4" /> <enum name="iconReturnKey" value="5" /> <enum name="iconSearchKey" value="6" /> - <!-- This is also represented as "@icon/7" in keyboard layout XML. --> <enum name="iconTabKey" value="7" /> <enum name="iconShortcutKey" value="8" /> <enum name="iconShortcutForLabel" value="9" /> diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 55f35f0c5..5e6804364 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -25,7 +25,8 @@ <bool name="config_enable_show_voice_key_option">true</bool> <bool name="config_enable_show_popup_on_keypress_option">true</bool> <bool name="config_enable_bigram_suggestions_option">true</bool> - <bool name="config_enable_usability_study_mode_option">false</bool> + <!-- TODO: Disable the following configuration for production. --> + <bool name="config_enable_usability_study_mode_option">true</bool> <bool name="config_sliding_key_input_enabled">true</bool> <bool name="config_digit_more_keys_enabled">true</bool> <!-- Whether or not Popup on key press is enabled by default --> diff --git a/java/res/values/donottranslate-more-keys.xml b/java/res/values/donottranslate-more-keys.xml index 3a3ea1e55..ce1538131 100644 --- a/java/res/values/donottranslate-more-keys.xml +++ b/java/res/values/donottranslate-more-keys.xml @@ -91,10 +91,8 @@ <string name="keylabel_for_symbols_percent">%</string> <string name="more_keys_for_comma"></string> <string name="more_keys_for_f1"></string> - <!-- @icon/3 is iconSettingsKey --> - <string name="more_keys_for_f1_settings">\@icon/3|\@integer/key_settings</string> - <!-- @icon/7 is iconTabKey --> - <string name="more_keys_for_f1_navigate">\@icon/7|\@integer/key_tab</string> + <string name="more_keys_for_f1_settings">\@icon/settingsKey|\@integer/key_settings</string> + <string name="more_keys_for_f1_navigate">\@icon/tabKey|\@integer/key_tab</string> <string name="more_keys_for_symbols_question">¿</string> <string name="more_keys_for_symbols_semicolon"></string> <string name="more_keys_for_symbols_percent">‰</string> diff --git a/java/res/xml-bg/keyboard_set.xml b/java/res/xml-bg/keyboard_set.xml new file mode 100644 index 000000000..a789de630 --- /dev/null +++ b/java/res/xml-bg/keyboard_set.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<KeyboardSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:keyboardLocale="bg"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_bulgarian" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShift" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneShift" + latin:elementKeyboard="@xml/kbd_phone_shift" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardSet> diff --git a/java/res/xml-sw600dp/kbd_key_styles.xml b/java/res/xml-sw600dp/kbd_key_styles.xml index ab90c9894..abba592b7 100644 --- a/java/res/xml-sw600dp/kbd_key_styles.xml +++ b/java/res/xml-sw600dp/kbd_key_styles.xml @@ -34,7 +34,7 @@ <key-style latin:styleName="f2PopupStyle" latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="\@icon/3|\@integer/key_settings" + latin:moreKeys="\@icon/settingsKey|\@integer/key_settings" latin:backgroundType="functional" /> </default> </switch> diff --git a/java/res/xml-sw600dp/kbd_rows_bulgarian.xml b/java/res/xml-sw600dp/kbd_rows_bulgarian.xml new file mode 100644 index 000000000..746398d49 --- /dev/null +++ b/java/res/xml-sw600dp/kbd_rows_bulgarian.xml @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/kbd_key_styles" /> + <Row + latin:keyWidth="7.692%p" + > + <Key + latin:keyLabel="ч" /> + <Key + latin:keyLabel="ш" /> + <Key + latin:keyLabel="е" /> + <Key + latin:keyLabel="р" /> + <Key + latin:keyLabel="т" /> + <Key + latin:keyLabel="ъ" /> + <Key + latin:keyLabel="у" /> + <Key + latin:keyLabel="и" + latin:moreKeys="ѝ" /> + <Key + latin:keyLabel="о" /> + <Key + latin:keyLabel="п" /> + <Key + latin:keyLabel="я" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillBoth" /> + </Row> + <Row + latin:keyWidth="7.692%p" + > + <Key + latin:keyLabel="а" + latin:keyXPos="4.000%p" /> + <Key + latin:keyLabel="с" /> + <Key + latin:keyLabel="д" /> + <Key + latin:keyLabel="ф" /> + <Key + latin:keyLabel="г" /> + <Key + latin:keyLabel="х" /> + <Key + latin:keyLabel="й" /> + <Key + latin:keyLabel="к" /> + <Key + latin:keyLabel="л" /> + <Key + latin:keyLabel="щ" /> + <Key + latin:keyLabel="ь" /> + <Key + latin:keyStyle="returnKeyStyle" + latin:keyWidth="fillBoth" /> + </Row> + <Row + latin:keyWidth="7.692%p" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.000%p" /> + <Key + latin:keyLabel="з" /> + <Key + latin:keyLabel="ж" /> + <Key + latin:keyLabel="ц" /> + <Key + latin:keyLabel="в" /> + <Key + latin:keyLabel="б" /> + <Key + latin:keyLabel="н" /> + <Key + latin:keyLabel="м" /> + <Key + latin:keyLabel="ю" /> + <switch> + <case + latin:mode="email" + > + <Key + latin:keyLabel="," /> + <Key + latin:keyLabel="." /> + </case> + <default> + <Key + latin:keyLabel="," + latin:keyLabelFlags="hasUppercaseLetter" + latin:keyHintLabel="!" + latin:moreKeys="!" /> + <Key + latin:keyLabel="." + latin:keyLabelFlags="hasUppercaseLetter" + latin:keyHintLabel="\?" + latin:moreKeys="\?" /> + </default> + </switch> + <Spacer + latin:keyXPos="-10.000%p" + latin:keyWidth="0%p" /> + <include + latin:keyboardLayout="@xml/kbd_row3_smiley" /> + </Row> + <include + latin:keyboardLayout="@xml/kbd_qwerty_row4" /> +</merge> diff --git a/java/res/xml-sw768dp/kbd_rows_bulgarian.xml b/java/res/xml-sw768dp/kbd_rows_bulgarian.xml new file mode 100644 index 000000000..42b3da525 --- /dev/null +++ b/java/res/xml-sw768dp/kbd_rows_bulgarian.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/kbd_key_styles" /> + <Row + latin:keyWidth="7.333%p" + > + <Key + latin:keyStyle="tabKeyStyle" + latin:keyLabelFlags="alignLeft" /> + <Key + latin:keyLabel="ч" /> + <Key + latin:keyLabel="ш" /> + <Key + latin:keyLabel="е" /> + <Key + latin:keyLabel="р" /> + <Key + latin:keyLabel="т" /> + <Key + latin:keyLabel="ъ" /> + <Key + latin:keyLabel="у" /> + <Key + latin:keyLabel="и" + latin:moreKeys="ѝ" /> + <Key + latin:keyLabel="о" /> + <Key + latin:keyLabel="п" /> + <Key + latin:keyLabel="я" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillBoth" /> + </Row> + <Row + latin:keyWidth="7.194%p" + > + <Key + latin:keyStyle="toSymbolKeyStyle" + latin:keyLabelFlags="alignLeft" + latin:keyWidth="9.375%p" /> + <Key + latin:keyLabel="а" /> + <Key + latin:keyLabel="с" /> + <Key + latin:keyLabel="д" /> + <Key + latin:keyLabel="ф" /> + <Key + latin:keyLabel="г" /> + <Key + latin:keyLabel="х" /> + <Key + latin:keyLabel="й" /> + <Key + latin:keyLabel="к" /> + <Key + latin:keyLabel="л" /> + <Key + latin:keyLabel="щ" /> + <Key + latin:keyLabel="ь" /> + <Key + latin:keyStyle="returnKeyStyle" + latin:keyWidth="fillBoth" /> + </Row> + <Row + latin:keyWidth="7.125%p" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="14.375%p" /> + <Key + latin:keyLabel="з" /> + <Key + latin:keyLabel="ж" /> + <Key + latin:keyLabel="ц" /> + <Key + latin:keyLabel="в" /> + <Key + latin:keyLabel="б" /> + <Key + latin:keyLabel="н" /> + <Key + latin:keyLabel="м" /> + <Key + latin:keyLabel="ю" /> + <include + latin:keyboardLayout="@xml/kbd_row3_comma_period" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="fillBoth" /> + </Row> + <include + latin:keyboardLayout="@xml/kbd_qwerty_row4" /> +</merge> diff --git a/java/res/xml/kbd_bulgarian.xml b/java/res/xml/kbd_bulgarian.xml new file mode 100644 index 000000000..f11438322 --- /dev/null +++ b/java/res/xml/kbd_bulgarian.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"): +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<Keyboard + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/kbd_rows_bulgarian" /> +</Keyboard> diff --git a/java/res/xml/kbd_rows_bulgarian.xml b/java/res/xml/kbd_rows_bulgarian.xml new file mode 100644 index 000000000..7b18361a9 --- /dev/null +++ b/java/res/xml/kbd_rows_bulgarian.xml @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/kbd_key_styles" /> + <Row + latin:keyWidth="9.091%p" + > + <Key + latin:keyLabel="ч" + latin:keyHintLabel="1" + latin:moreKeys="1" /> + <Key + latin:keyLabel="ш" + latin:keyHintLabel="2" + latin:moreKeys="2" /> + <Key + latin:keyLabel="е" + latin:keyHintLabel="3" + latin:moreKeys="3" /> + <Key + latin:keyLabel="р" + latin:keyHintLabel="4" + latin:moreKeys="4" /> + <Key + latin:keyLabel="т" + latin:keyHintLabel="5" + latin:moreKeys="5" /> + <Key + latin:keyLabel="ъ" + latin:keyHintLabel="6" + latin:moreKeys="6" /> + <Key + latin:keyLabel="у" + latin:keyHintLabel="7" + latin:moreKeys="7" /> + <Key + latin:keyLabel="и" + latin:keyHintLabel="8" + latin:moreKeys="8,ѝ" /> + <Key + latin:keyLabel="о" + latin:keyHintLabel="9" + latin:moreKeys="9" /> + <Key + latin:keyLabel="п" + latin:keyHintLabel="0" + latin:moreKeys="0" /> + <Key + latin:keyLabel="я" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="9.091%p" + > + <Key + latin:keyLabel="а" /> + <Key + latin:keyLabel="с" /> + <Key + latin:keyLabel="д" /> + <Key + latin:keyLabel="ф" /> + <Key + latin:keyLabel="г" /> + <Key + latin:keyLabel="х" /> + <Key + latin:keyLabel="й" /> + <Key + latin:keyLabel="к" /> + <Key + latin:keyLabel="л" /> + <Key + latin:keyLabel="щ" /> + <Key + latin:keyLabel="ь" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="9.091%p" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="13.636%p" /> + <Key + latin:keyLabel="з" /> + <Key + latin:keyLabel="ж" /> + <Key + latin:keyLabel="ц" /> + <Key + latin:keyLabel="в" /> + <Key + latin:keyLabel="б" /> + <Key + latin:keyLabel="н" /> + <Key + latin:keyLabel="м" /> + <Key + latin:keyLabel="ю" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <include + latin:keyboardLayout="@xml/kbd_qwerty_row4" /> +</merge> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 6c827363c..650f91b9b 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -20,8 +20,8 @@ <!-- The attributes in this XML file provide configuration information --> <!-- for the Input Method Manager. --> -<!-- Keyboard: en_US, en_GB, ar, be, cs, da, de, de(QWERTY), es, es_US, et, fi, fr, fr_CA, fr_CH, - hr, hu, it, iw, ky, lt, lv, nb, nl, pl, pt, ro, ru, sk, sl, sr, sv, tr, uk, vi --> +<!-- Keyboard: en_US, en_GB, ar, be, bg, cs, da, de, de(QWERTY), es, es_US, et, fi, fr, fr_CA, + fr_CH, hr, hu, it, iw, ky, lt, lv, nb, nl, pl, pt, ro, ru, sk, sl, sr, sv, tr, uk, vi --> <!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. --> <!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default subtype.--> @@ -54,6 +54,12 @@ /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" + android:imeSubtypeLocale="bg" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection" + /> + <subtype android:icon="@drawable/ic_subtype_keyboard" + android:label="@string/subtype_generic" android:imeSubtypeLocale="cs" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index ebd61505d..3a9423f4b 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -101,7 +101,7 @@ public class Key { /** Text to output when pressed. This can be multiple characters, like ".com" */ public final CharSequence mOutputText; /** More keys */ - public final CharSequence[] mMoreKeys; + public final String[] mMoreKeys; /** More keys maximum column number */ public final int mMaxMoreKeysColumn; @@ -166,7 +166,12 @@ public class Key { } private static Drawable getIcon(Keyboard.Params params, String moreKeySpec) { - return params.mIconsSet.getIconByIconId(MoreKeySpecParser.getIconId(moreKeySpec)); + final int iconAttrId = MoreKeySpecParser.getIconAttrId(moreKeySpec); + if (iconAttrId == KeyboardIconsSet.ICON_UNDEFINED) { + return null; + } else { + return params.mIconsSet.getIconByAttrId(iconAttrId); + } } /** @@ -255,7 +260,7 @@ public class Key { // Update row to have current x coordinate. row.setXPos(keyXPos + keyWidth); - final CharSequence[] moreKeys = style.getTextArray(keyAttr, + final String[] moreKeys = style.getTextArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); // In Arabic symbol layouts, we'd like to keep digits in more keys regardless of // config_digit_more_keys_enabled. @@ -277,12 +282,15 @@ public class Key { R.styleable.Keyboard_Key_visualInsetsLeft, params.mBaseWidth, 0); mVisualInsetsRight = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr, R.styleable.Keyboard_Key_visualInsetsRight, params.mBaseWidth, 0); - mPreviewIcon = iconsSet.getIconByIconId(style.getInt(keyAttr, + final int previewIconAttrId = KeyboardIconsSet.getIconAttrId(style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconPreview, KeyboardIconsSet.ICON_UNDEFINED)); - mIcon = iconsSet.getIconByIconId(style.getInt(keyAttr, + mPreviewIcon = iconsSet.getIconByAttrId(previewIconAttrId); + final int iconAttrId = KeyboardIconsSet.getIconAttrId(style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIcon, KeyboardIconsSet.ICON_UNDEFINED)); - mDisabledIcon = iconsSet.getIconByIconId(style.getInt(keyAttr, + mIcon = iconsSet.getIconByAttrId(iconAttrId); + final int disabledIconAttrId = KeyboardIconsSet.getIconAttrId(style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconDisabled, KeyboardIconsSet.ICON_UNDEFINED)); + mDisabledIcon = iconsSet.getIconByAttrId(disabledIconAttrId); mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 22ab3e4e5..4967a5e80 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -137,8 +137,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, mKeyboardSet = builder.build(); final KeyboardId mainKeyboardId = mKeyboardSet.getMainKeyboardId(); try { - mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols), - hasDistinctMultitouch()); + mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols)); } catch (RuntimeException e) { Log.w(TAG, "loading keyboard failed: " + mainKeyboardId, e); LatinImeLogger.logOnException(mainKeyboardId.toString(), e); @@ -316,7 +315,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, } /** - * Updates state machine to figure out when to automatically snap back to the previous mode. + * Updates state machine to figure out when to automatically switch back to the previous mode. */ public void onCodeInput(int code) { mState.onCodeInput(code, isSinglePointer(), mInputMethodService.getCurrentAutoCapsState()); diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java index 548b5ea85..974291373 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java @@ -34,7 +34,7 @@ public class MiniKeyboard extends Keyboard { } public static class Builder extends Keyboard.Builder<Builder.MiniKeyboardParams> { - private final CharSequence[] mMoreKeys; + private final String[] mMoreKeys; public static class MiniKeyboardParams extends Keyboard.Params { /* package */int mTopRowAdjustment; @@ -230,16 +230,14 @@ public class MiniKeyboard extends Keyboard { parentKey.mX + (mParams.mDefaultKeyWidth - width) / 2, view.getMeasuredWidth()); } - private static int getMaxKeyWidth(KeyboardView view, CharSequence[] moreKeys, - int minKeyWidth) { + private static int getMaxKeyWidth(KeyboardView view, String[] moreKeys, int minKeyWidth) { final int padding = (int) view.getContext().getResources() .getDimension(R.dimen.mini_keyboard_key_horizontal_padding); Paint paint = null; int maxWidth = minKeyWidth; - for (CharSequence moreKeySpec : moreKeys) { - final CharSequence label = MoreKeySpecParser.getLabel(moreKeySpec.toString()); - // If the label is single letter, minKeyWidth is enough to hold - // the label. + for (String moreKeySpec : moreKeys) { + final String label = MoreKeySpecParser.getLabel(moreKeySpec); + // If the label is single letter, minKeyWidth is enough to hold the label. if (label != null && label.length() > 1) { if (paint == null) { paint = new Paint(); @@ -258,7 +256,7 @@ public class MiniKeyboard extends Keyboard { public MiniKeyboard build() { final MiniKeyboardParams params = mParams; for (int n = 0; n < mMoreKeys.length; n++) { - final String moreKeySpec = mMoreKeys[n].toString(); + final String moreKeySpec = mMoreKeys[n]; final int row = n / params.mNumColumns; final Key key = new Key(mResources, params, moreKeySpec, params.getX(n, row), params.getY(row), params.mDefaultKeyWidth, params.mDefaultRowHeight); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 5dd8340fc..faea38941 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -16,11 +16,13 @@ package com.android.inputmethod.keyboard.internal; +import android.content.res.Resources; import android.content.res.TypedArray; import android.util.Log; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.XmlParseUtils; import org.xmlpull.v1.XmlPullParser; @@ -30,7 +32,7 @@ import java.util.ArrayList; import java.util.HashMap; public class KeyStyles { - private static final String TAG = "KeyStyles"; + private static final String TAG = KeyStyles.class.getSimpleName(); private static final boolean DEBUG = false; private final HashMap<String, DeclaredKeyStyle> mStyles = @@ -38,19 +40,19 @@ public class KeyStyles { private static final KeyStyle EMPTY_KEY_STYLE = new EmptyKeyStyle(); public interface KeyStyle { - public CharSequence[] getTextArray(TypedArray a, int index); + public String[] getTextArray(TypedArray a, int index); 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); } - /* package */ static class EmptyKeyStyle implements KeyStyle { + private static class EmptyKeyStyle implements KeyStyle { EmptyKeyStyle() { // Nothing to do. } @Override - public CharSequence[] getTextArray(TypedArray a, int index) { + public String[] getTextArray(TypedArray a, int index) { return parseTextArray(a, index); } @@ -69,64 +71,77 @@ public class KeyStyles { return a.getInt(index, defaultValue); } - protected static CharSequence[] parseTextArray(TypedArray a, int index) { + protected static String[] parseTextArray(TypedArray a, int index) { if (!a.hasValue(index)) return null; final CharSequence text = a.getText(index); - return parseCsvText(text); - } - - /* package */ static CharSequence[] parseCsvText(CharSequence text) { - final int size = text.length(); - if (size == 0) return null; - if (size == 1) return new CharSequence[] { text }; - final StringBuilder sb = new StringBuilder(); - ArrayList<CharSequence> list = null; - int start = 0; - for (int pos = 0; pos < size; pos++) { - final char c = text.charAt(pos); - if (c == ',') { - if (list == null) list = new ArrayList<CharSequence>(); - if (sb.length() == 0) { - list.add(text.subSequence(start, pos)); - } else { - list.add(sb.toString()); - sb.setLength(0); + return parseCsvText(text.toString(), a.getResources(), R.string.english_ime_name); + } + } + + /* package for test */ + static String[] parseCsvText(String rawText, Resources res, int packageNameResId) { + final String text = Utils.resolveStringResource(rawText, res, packageNameResId); + final int size = text.length(); + if (size == 0) { + return null; + } + if (size == 1) { + return new String[] { text }; + } + + final StringBuilder sb = new StringBuilder(); + ArrayList<String> list = null; + int start = 0; + for (int pos = 0; pos < size; pos++) { + final char c = text.charAt(pos); + if (c == ',') { + if (list == null) { + list = new ArrayList<String>(); + } + if (sb.length() == 0) { + list.add(text.substring(start, pos)); + } else { + list.add(sb.toString()); + sb.setLength(0); + } + start = pos + 1; + continue; + } else if (c == Utils.ESCAPE_CHAR) { + if (start == pos) { + // Skip escape character at the beginning of the value. + start++; + pos++; + } else { + if (start < pos && sb.length() == 0) { + sb.append(text.subSequence(start, pos)); } - start = pos + 1; - continue; - } else if (c == '\\') { - if (start == pos) { - // Skip escape character at the beginning of the value. - start++; - pos++; - } else { - if (start < pos && sb.length() == 0) - sb.append(text.subSequence(start, pos)); - pos++; - if (pos < size) - sb.append(text.charAt(pos)); + pos++; + if (pos < size) { + sb.append(text.charAt(pos)); } - } else if (sb.length() > 0) { - sb.append(c); } + } else if (sb.length() > 0) { + sb.append(c); } - if (list == null) { - return new CharSequence[] { sb.length() > 0 ? sb : text.subSequence(start, size) }; - } else { - list.add(sb.length() > 0 ? sb : text.subSequence(start, size)); - return list.toArray(new CharSequence[list.size()]); - } + } + if (list == null) { + return new String[] { + sb.length() > 0 ? sb.toString() : text.substring(start) + }; + } else { + list.add(sb.length() > 0 ? sb.toString() : text.substring(start)); + return list.toArray(new String[list.size()]); } } - /* package */ static class DeclaredKeyStyle extends EmptyKeyStyle { + private static class DeclaredKeyStyle extends EmptyKeyStyle { private final HashMap<Integer, Object> mAttributes = new HashMap<Integer, Object>(); @Override - public CharSequence[] getTextArray(TypedArray a, int index) { + public String[] getTextArray(TypedArray a, int index) { return a.hasValue(index) - ? super.getTextArray(a, index) : (CharSequence[])mAttributes.get(index); + ? super.getTextArray(a, index) : (String[])mAttributes.get(index); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 6313a61b5..09ecbcaa0 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -31,34 +31,36 @@ public class KeyboardIconsSet { private static final String TAG = KeyboardIconsSet.class.getSimpleName(); public static final int ICON_UNDEFINED = 0; + private static final int ATTR_UNDEFINED = 0; private final Map<Integer, Drawable> mIcons = new HashMap<Integer, Drawable>(); // The key value should be aligned with the enum value of Keyboard.icon*. private static final Map<Integer, Integer> ICONS_TO_ATTRS_MAP = new HashMap<Integer, Integer>(); + private static final Map<String, Integer> NAME_TO_ATTRS_MAP = new HashMap<String, Integer>(); private static final Collection<Integer> VALID_ATTRS; static { - addIconIdMap(1, R.styleable.Keyboard_iconShiftKey); - addIconIdMap(2, R.styleable.Keyboard_iconDeleteKey); - // This is also represented as "@icon/3" in keyboard layout XML. - addIconIdMap(3, R.styleable.Keyboard_iconSettingsKey); - addIconIdMap(4, R.styleable.Keyboard_iconSpaceKey); - addIconIdMap(5, R.styleable.Keyboard_iconReturnKey); - addIconIdMap(6, R.styleable.Keyboard_iconSearchKey); - // This is also represented as "@icon/7" in keyboard layout XML. - addIconIdMap(7, R.styleable.Keyboard_iconTabKey); - addIconIdMap(8, R.styleable.Keyboard_iconShortcutKey); - addIconIdMap(9, R.styleable.Keyboard_iconShortcutForLabel); - addIconIdMap(10, R.styleable.Keyboard_iconSpaceKeyForNumberLayout); - addIconIdMap(11, R.styleable.Keyboard_iconShiftKeyShifted); - addIconIdMap(12, R.styleable.Keyboard_iconDisabledShortcutKey); - addIconIdMap(13, R.styleable.Keyboard_iconPreviewTabKey); + addIconIdMap(1, "shiftKey", R.styleable.Keyboard_iconShiftKey); + addIconIdMap(2, "deleteKey", R.styleable.Keyboard_iconDeleteKey); + addIconIdMap(3, "settingsKey", R.styleable.Keyboard_iconSettingsKey); + addIconIdMap(4, "spaceKey", R.styleable.Keyboard_iconSpaceKey); + addIconIdMap(5, "returnKey", R.styleable.Keyboard_iconReturnKey); + addIconIdMap(6, "searchKey", R.styleable.Keyboard_iconSearchKey); + addIconIdMap(7, "tabKey", R.styleable.Keyboard_iconTabKey); + addIconIdMap(8, "shortcutKey", R.styleable.Keyboard_iconShortcutKey); + addIconIdMap(9, "shortcutForLabel", R.styleable.Keyboard_iconShortcutForLabel); + addIconIdMap(10, "spaceKeyForNumberLayout", + R.styleable.Keyboard_iconSpaceKeyForNumberLayout); + addIconIdMap(11, "shiftKeyShifted", R.styleable.Keyboard_iconShiftKeyShifted); + addIconIdMap(12, "disabledShortcurKey", R.styleable.Keyboard_iconDisabledShortcutKey); + addIconIdMap(13, "previewTabKey", R.styleable.Keyboard_iconPreviewTabKey); VALID_ATTRS = ICONS_TO_ATTRS_MAP.values(); } - private static void addIconIdMap(int iconId, int attrId) { + private static void addIconIdMap(int iconId, String name, Integer attrId) { ICONS_TO_ATTRS_MAP.put(iconId, attrId); + NAME_TO_ATTRS_MAP.put(name, attrId); } public void loadIcons(final TypedArray keyboardAttrs) { @@ -76,18 +78,29 @@ public class KeyboardIconsSet { } } - public Drawable getIconByIconId(final Integer iconId) { + public static int getIconAttrId(final Integer iconId) { if (iconId == ICON_UNDEFINED) { - return null; + return ATTR_UNDEFINED; } final Integer attrId = ICONS_TO_ATTRS_MAP.get(iconId); if (attrId == null) { throw new IllegalArgumentException("icon id is out of range: " + iconId); } - return getIconByAttrId(attrId); + return attrId; + } + + public static int getIconAttrId(final String iconName) { + final Integer attrId = NAME_TO_ATTRS_MAP.get(iconName); + if (attrId == null) { + throw new IllegalArgumentException("unknown icon name: " + iconName); + } + return attrId; } public Drawable getIconByAttrId(final Integer attrId) { + if (attrId == ATTR_UNDEFINED) { + return null; + } if (!VALID_ATTRS.contains(attrId)) { throw new IllegalArgumentException("unknown icon attribute id: " + attrId); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index c43b9852b..af16e4907 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -26,7 +26,7 @@ import com.android.inputmethod.keyboard.Keyboard; * * This class contains all keyboard state transition logic. * - * The input events are {@link #onLoadKeyboard(String, boolean)}, {@link #onSaveKeyboardState()}, + * The input events are {@link #onLoadKeyboard(String)}, {@link #onSaveKeyboardState()}, * {@link #onPressKey(int)}, {@link #onReleaseKey(int, boolean)}, * {@link #onCodeInput(int, boolean, boolean)}, {@link #onCancelInput(boolean)}, * {@link #onUpdateShiftState(boolean)}. @@ -58,7 +58,7 @@ public class KeyboardState { public void requestUpdatingShiftState(); } - private KeyboardShiftState mKeyboardShiftState = new KeyboardShiftState(); + private final SwitchActions mSwitchActions; private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift"); private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol"); @@ -72,17 +72,15 @@ public class KeyboardState { private static final int SWITCH_STATE_CHORDING_ALPHA = 5; private static final int SWITCH_STATE_CHORDING_SYMBOL = 6; private int mSwitchState = SWITCH_STATE_ALPHA; - private String mLayoutSwitchBackSymbols; - private boolean mHasDistinctMultitouch; - - private final SwitchActions mSwitchActions; private boolean mIsAlphabetMode; + private KeyboardShiftState mAlphabetShiftState = new KeyboardShiftState(); private boolean mIsSymbolShifted; + private boolean mPrevMainKeyboardWasShiftLocked; + private boolean mPrevSymbolsKeyboardWasShifted; private final SavedKeyboardState mSavedKeyboardState = new SavedKeyboardState(); - private boolean mPrevMainKeyboardWasShiftLocked; static class SavedKeyboardState { public boolean mIsValid; @@ -95,17 +93,17 @@ public class KeyboardState { mSwitchActions = switchActions; } - public void onLoadKeyboard(String layoutSwitchBackSymbols, boolean hasDistinctMultitouch) { + public void onLoadKeyboard(String layoutSwitchBackSymbols) { if (DEBUG_EVENT) { Log.d(TAG, "onLoadKeyboard"); } mLayoutSwitchBackSymbols = layoutSwitchBackSymbols; - mHasDistinctMultitouch = hasDistinctMultitouch; - mKeyboardShiftState.setShifted(false); - mKeyboardShiftState.setShiftLocked(false); + // Reset alphabet shift state. + mAlphabetShiftState.setShiftLocked(false); + mPrevMainKeyboardWasShiftLocked = false; + mPrevSymbolsKeyboardWasShifted = false; mShiftKeyState.onRelease(); mSymbolKeyState.onRelease(); - mPrevMainKeyboardWasShiftLocked = false; onRestoreKeyboardState(); } @@ -113,9 +111,9 @@ public class KeyboardState { final SavedKeyboardState state = mSavedKeyboardState; state.mIsAlphabetMode = mIsAlphabetMode; if (mIsAlphabetMode) { - state.mIsShiftLocked = mKeyboardShiftState.isShiftLocked(); + state.mIsShiftLocked = mAlphabetShiftState.isShiftLocked(); state.mIsShifted = !state.mIsShiftLocked - && mKeyboardShiftState.isShiftedOrShiftLocked(); + && mAlphabetShiftState.isShiftedOrShiftLocked(); } else { state.mIsShiftLocked = false; state.mIsShifted = mIsSymbolShifted; @@ -157,25 +155,23 @@ public class KeyboardState { // TODO: Remove this method. public boolean isShiftLocked() { - return mKeyboardShiftState.isShiftLocked(); + return mAlphabetShiftState.isShiftLocked(); } private void setShifted(int shiftMode) { if (DEBUG_ACTION) { Log.d(TAG, "setShifted: shiftMode=" + shiftModeToString(shiftMode)); } - if (shiftMode == SwitchActions.AUTOMATIC_SHIFT) { - mKeyboardShiftState.setAutomaticTemporaryUpperCase(); - } else { - final boolean shifted = (shiftMode == SwitchActions.MANUAL_SHIFT); - // On non-distinct multi touch panel device, we should also turn off the shift locked - // state when shift key is pressed to go to normal mode. - // On the other hand, on distinct multi touch panel device, turning off the shift - // locked state with shift key pressing is handled by onReleaseShift(). - if (!mHasDistinctMultitouch && !shifted && mKeyboardShiftState.isShiftLocked()) { - mSwitchActions.setShiftLocked(false); - } - mKeyboardShiftState.setShifted(shifted); + switch (shiftMode) { + case SwitchActions.AUTOMATIC_SHIFT: + mAlphabetShiftState.setAutomaticTemporaryUpperCase(); + break; + case SwitchActions.MANUAL_SHIFT: + mAlphabetShiftState.setShifted(true); + break; + case SwitchActions.UNSHIFT: + mAlphabetShiftState.setShifted(false); + break; } mSwitchActions.setShifted(shiftMode); } @@ -184,7 +180,7 @@ public class KeyboardState { if (DEBUG_ACTION) { Log.d(TAG, "setShiftLocked: shiftLocked=" + shiftLocked); } - mKeyboardShiftState.setShiftLocked(shiftLocked); + mAlphabetShiftState.setShiftLocked(shiftLocked); mSwitchActions.setShiftLocked(shiftLocked); } @@ -208,6 +204,7 @@ public class KeyboardState { if (DEBUG_ACTION) { Log.d(TAG, "setAlphabetKeyboard"); } + mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted; mSwitchActions.setAlphabetKeyboard(); mIsAlphabetMode = true; mIsSymbolShifted = false; @@ -219,13 +216,21 @@ public class KeyboardState { // TODO: Make this method private public void setSymbolsKeyboard() { + mPrevMainKeyboardWasShiftLocked = mAlphabetShiftState.isShiftLocked(); + if (mPrevSymbolsKeyboardWasShifted) { + setSymbolsShiftedKeyboard(); + return; + } + if (DEBUG_ACTION) { Log.d(TAG, "setSymbolsKeyboard"); } - mPrevMainKeyboardWasShiftLocked = mKeyboardShiftState.isShiftLocked(); mSwitchActions.setSymbolsKeyboard(); mIsAlphabetMode = false; mIsSymbolShifted = false; + // Reset alphabet shift state. + mAlphabetShiftState.setShiftLocked(false); + mPrevSymbolsKeyboardWasShifted = false; mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; } @@ -236,6 +241,9 @@ public class KeyboardState { mSwitchActions.setSymbolsShiftedKeyboard(); mIsAlphabetMode = false; mIsSymbolShifted = true; + // Reset alphabet shift state. + mAlphabetShiftState.setShiftLocked(false); + mPrevSymbolsKeyboardWasShifted = false; mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; } @@ -273,7 +281,7 @@ public class KeyboardState { } private void onReleaseSymbol() { - // Snap back to the previous keyboard mode if the user chords the mode change key and + // Switch back to the previous keyboard mode if the user chords the mode change key and // another key, then releases the mode change key. if (mSwitchState == SWITCH_STATE_CHORDING_ALPHA) { toggleAlphabetAndSymbols(); @@ -290,7 +298,7 @@ public class KeyboardState { private void onUpdateShiftStateInternal(boolean autoCaps) { if (mIsAlphabetMode) { - if (!mKeyboardShiftState.isShiftLocked() && !mShiftKeyState.isIgnoring()) { + if (!mAlphabetShiftState.isShiftLocked() && !mShiftKeyState.isIgnoring()) { if (mShiftKeyState.isReleasing() && autoCaps) { // Only when shift key is releasing, automatic temporary upper case will be set. setShifted(SwitchActions.AUTOMATIC_SHIFT); @@ -308,17 +316,17 @@ public class KeyboardState { private void onPressShift() { if (mIsAlphabetMode) { - if (mKeyboardShiftState.isShiftLocked()) { + if (mAlphabetShiftState.isShiftLocked()) { // Shift key is pressed while caps lock state, we will treat this state as shifted // caps lock state and mark as if shift key pressed while normal state. setShifted(SwitchActions.MANUAL_SHIFT); mShiftKeyState.onPress(); - } else if (mKeyboardShiftState.isAutomaticTemporaryUpperCase()) { + } else if (mAlphabetShiftState.isAutomaticTemporaryUpperCase()) { // Shift key is pressed while automatic temporary upper case, we have to move to // manual temporary upper case. setShifted(SwitchActions.MANUAL_SHIFT); mShiftKeyState.onPress(); - } else if (mKeyboardShiftState.isShiftedOrShiftLocked()) { + } else if (mAlphabetShiftState.isShiftedOrShiftLocked()) { // In manual upper case state, we just record shift key has been pressing while // shifted state. mShiftKeyState.onPressOnShifted(); @@ -337,30 +345,34 @@ public class KeyboardState { private void onReleaseShift(boolean withSliding) { if (mIsAlphabetMode) { - final boolean isShiftLocked = mKeyboardShiftState.isShiftLocked(); + final boolean isShiftLocked = mAlphabetShiftState.isShiftLocked(); if (mShiftKeyState.isMomentary()) { // After chording input while normal state. - setShifted(SwitchActions.UNSHIFT); - } else if (isShiftLocked && !mKeyboardShiftState.isShiftLockShifted() + if (mAlphabetShiftState.isShiftLockShifted()) { + setShiftLocked(true); + } else { + setShifted(SwitchActions.UNSHIFT); + } + } else if (isShiftLocked && !mAlphabetShiftState.isShiftLockShifted() && (mShiftKeyState.isPressing() || mShiftKeyState.isPressingOnShifted()) && !withSliding) { // Shift has been long pressed, ignore this release. } else if (isShiftLocked && !mShiftKeyState.isIgnoring() && !withSliding) { // Shift has been pressed without chording while caps lock state. setShiftLocked(false); - } else if (mKeyboardShiftState.isShiftedOrShiftLocked() + } else if (mAlphabetShiftState.isShiftedOrShiftLocked() && mShiftKeyState.isPressingOnShifted() && !withSliding) { // Shift has been pressed without chording while shifted state. setShifted(SwitchActions.UNSHIFT); - } else if (mKeyboardShiftState.isManualTemporaryUpperCaseFromAuto() + } else if (mAlphabetShiftState.isManualTemporaryUpperCaseFromAuto() && mShiftKeyState.isPressing() && !withSliding) { // Shift has been pressed without chording while manual temporary upper case // transited from automatic temporary upper case. setShifted(SwitchActions.UNSHIFT); } } else { - // In symbol mode, snap back to the previous keyboard mode if the user chords the shift - // key and another key, then releases the shift key. + // In symbol mode, switch back to the previous keyboard mode if the user chords the + // shift key and another key, then releases the shift key. if (mSwitchState == SWITCH_STATE_CHORDING_SYMBOL) { toggleShiftInSymbols(); } @@ -372,7 +384,7 @@ public class KeyboardState { if (DEBUG_EVENT) { Log.d(TAG, "onCancelInput: single=" + isSinglePointer + " " + this); } - // Snap back to the previous keyboard mode if the user cancels sliding input. + // Switch back to the previous keyboard mode if the user cancels sliding input. if (isSinglePointer) { if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) { toggleAlphabetAndSymbols(); @@ -405,7 +417,7 @@ public class KeyboardState { } if (mIsAlphabetMode && code == Keyboard.CODE_CAPSLOCK) { - if (mKeyboardShiftState.isShiftLocked()) { + if (mAlphabetShiftState.isShiftLocked()) { setShiftLocked(false); // Shift key is long pressed or double tapped while caps lock state, we will // toggle back to normal state. And mark as if shift key is released. @@ -431,13 +443,13 @@ public class KeyboardState { mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; } } else if (isSinglePointer) { - // Snap back to the previous keyboard mode if the user pressed the mode change key + // Switch back to the previous keyboard mode if the user pressed the mode change key // and slid to other key, then released the finger. - // If the user cancels the sliding input, snapping back to the previous keyboard + // If the user cancels the sliding input, switching back to the previous keyboard // mode is handled by {@link #onCancelInput}. toggleAlphabetAndSymbols(); } else { - // Chording input is being started. The keyboard mode will be snapped back to the + // Chording input is being started. The keyboard mode will be switched back to the // previous mode in {@link onReleaseSymbol} when the mode change key is released. mSwitchState = SWITCH_STATE_CHORDING_ALPHA; } @@ -447,12 +459,12 @@ public class KeyboardState { // Detected only the shift key has been pressed on symbol layout, and then released. mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; } else if (isSinglePointer) { - // Snap back to the previous keyboard mode if the user pressed the shift key on + // Switch back to the previous keyboard mode if the user pressed the shift key on // symbol mode and slid to other key, then released the finger. toggleShiftInSymbols(); mSwitchState = SWITCH_STATE_SYMBOL; } else { - // Chording input is being started. The keyboard mode will be snapped back to the + // Chording input is being started. The keyboard mode will be switched back to the // previous mode in {@link onReleaseShift} when the shift key is released. mSwitchState = SWITCH_STATE_CHORDING_SYMBOL; } @@ -462,14 +474,14 @@ public class KeyboardState { || code == Keyboard.CODE_OUTPUT_TEXT)) { mSwitchState = SWITCH_STATE_SYMBOL; } - // Snap back to alpha keyboard mode immediately if user types a quote character. + // Switch back to alpha keyboard mode immediately if user types a quote character. if (isLayoutSwitchBackCharacter(code)) { setAlphabetKeyboard(); } break; case SWITCH_STATE_SYMBOL: case SWITCH_STATE_CHORDING_SYMBOL: - // Snap back to alpha keyboard mode if user types one or more non-space/enter + // Switch back to alpha keyboard mode if user types one or more non-space/enter // characters followed by a space/enter or a quote character. if (isSpaceCharacter(code) || isLayoutSwitchBackCharacter(code)) { setAlphabetKeyboard(); @@ -507,7 +519,8 @@ public class KeyboardState { @Override public String toString() { - return "[keyboard=" + mKeyboardShiftState + return "[keyboard=" + (mIsAlphabetMode ? mAlphabetShiftState.toString() + : (mIsSymbolShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS")) + " shift=" + mShiftKeyState + " symbol=" + mSymbolKeyState + " switch=" + switchStateToString(mSwitchState) + "]"; diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParser.java index 93be31ed9..16777733e 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParser.java @@ -18,10 +18,10 @@ package com.android.inputmethod.keyboard.internal; import android.content.res.Resources; import android.text.TextUtils; -import android.util.Log; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.Utils; import java.util.ArrayList; @@ -31,20 +31,16 @@ import java.util.ArrayList; * Each "more key" specification is one of the following: * - A single letter (Letter) * - Label optionally followed by keyOutputText or code (keyLabel|keyOutputText). - * - Icon followed by keyOutputText or code (@icon/icon_number|@integer/key_code) + * - Icon followed by keyOutputText or code (@icon/icon_name|@integer/key_code) * Special character, comma ',' backslash '\', and bar '|' can be escaped by '\' * character. * Note that the character '@' and '\' are also parsed by XML parser and CSV parser as well. * See {@link KeyboardIconsSet} about icon_number. */ public class MoreKeySpecParser { - private static final String TAG = MoreKeySpecParser.class.getSimpleName(); - - private static final char ESCAPE = '\\'; - private static final String LABEL_END = "|"; - private static final String PREFIX_AT = "@"; - private static final String PREFIX_ICON = PREFIX_AT + "icon/"; - private static final String PREFIX_CODE = PREFIX_AT + "integer/"; + private static final char LABEL_END = '|'; + private static final String PREFIX_ICON = Utils.PREFIX_AT + "icon" + Utils.SUFFIX_SLASH; + private static final String PREFIX_CODE = Utils.PREFIX_AT + "integer" + Utils.SUFFIX_SLASH; private MoreKeySpecParser() { // Intentional empty constructor for utility class. @@ -53,8 +49,9 @@ public class MoreKeySpecParser { private static boolean hasIcon(String moreKeySpec) { if (moreKeySpec.startsWith(PREFIX_ICON)) { final int end = indexOfLabelEnd(moreKeySpec, 0); - if (end > 0) + if (end > 0) { return true; + } throw new MoreKeySpecParserError("outputText or code not specified: " + moreKeySpec); } return false; @@ -70,13 +67,14 @@ public class MoreKeySpecParser { } private static String parseEscape(String text) { - if (text.indexOf(ESCAPE) < 0) + if (text.indexOf(Utils.ESCAPE_CHAR) < 0) { return text; + } final int length = text.length(); final StringBuilder sb = new StringBuilder(); for (int pos = 0; pos < length; pos++) { final char c = text.charAt(pos); - if (c == ESCAPE && pos + 1 < length) { + if (c == Utils.ESCAPE_CHAR && pos + 1 < length) { sb.append(text.charAt(++pos)); } else { sb.append(c); @@ -86,18 +84,19 @@ public class MoreKeySpecParser { } private static int indexOfLabelEnd(String moreKeySpec, int start) { - if (moreKeySpec.indexOf(ESCAPE, start) < 0) { + if (moreKeySpec.indexOf(Utils.ESCAPE_CHAR, start) < 0) { final int end = moreKeySpec.indexOf(LABEL_END, start); - if (end == 0) + if (end == 0) { throw new MoreKeySpecParserError(LABEL_END + " at " + start + ": " + moreKeySpec); + } return end; } final int length = moreKeySpec.length(); for (int pos = start; pos < length; pos++) { final char c = moreKeySpec.charAt(pos); - if (c == ESCAPE && pos + 1 < length) { + if (c == Utils.ESCAPE_CHAR && pos + 1 < length) { pos++; - } else if (moreKeySpec.startsWith(LABEL_END, pos)) { + } else if (c == LABEL_END) { return pos; } } @@ -105,79 +104,75 @@ public class MoreKeySpecParser { } public static String getLabel(String moreKeySpec) { - if (hasIcon(moreKeySpec)) + if (hasIcon(moreKeySpec)) { return null; + } final int end = indexOfLabelEnd(moreKeySpec, 0); final String label = (end > 0) ? parseEscape(moreKeySpec.substring(0, end)) : parseEscape(moreKeySpec); - if (TextUtils.isEmpty(label)) + if (TextUtils.isEmpty(label)) { throw new MoreKeySpecParserError("Empty label: " + moreKeySpec); + } return label; } public static String getOutputText(String moreKeySpec) { - if (hasCode(moreKeySpec)) + if (hasCode(moreKeySpec)) { return null; + } final int end = indexOfLabelEnd(moreKeySpec, 0); if (end > 0) { - if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) + if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) { throw new MoreKeySpecParserError("Multiple " + LABEL_END + ": " + moreKeySpec); - final String outputText = parseEscape(moreKeySpec.substring(end + LABEL_END.length())); - if (!TextUtils.isEmpty(outputText)) + } + final String outputText = parseEscape( + moreKeySpec.substring(end + /* LABEL_END */1)); + if (!TextUtils.isEmpty(outputText)) { return outputText; + } throw new MoreKeySpecParserError("Empty outputText: " + moreKeySpec); } final String label = getLabel(moreKeySpec); - if (label == null) + if (label == null) { throw new MoreKeySpecParserError("Empty label: " + moreKeySpec); + } // Code is automatically generated for one letter label. See {@link getCode()}. - if (label.length() == 1) - return null; - return label; + return (label.length() == 1) ? null : label; } public static int getCode(Resources res, String moreKeySpec) { if (hasCode(moreKeySpec)) { final int end = indexOfLabelEnd(moreKeySpec, 0); - if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) + if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) { throw new MoreKeySpecParserError("Multiple " + LABEL_END + ": " + moreKeySpec); - final int resId = getResourceId(res, - moreKeySpec.substring(end + LABEL_END.length() + PREFIX_AT.length())); + } + final int resId = Utils.getResourceId(res, + moreKeySpec.substring(end + /* LABEL_END */1 + /* PREFIX_AT */1), + R.string.english_ime_name); final int code = res.getInteger(resId); return code; } - if (indexOfLabelEnd(moreKeySpec, 0) > 0) - return Keyboard.CODE_UNSPECIFIED; + if (indexOfLabelEnd(moreKeySpec, 0) > 0) { + return Keyboard.CODE_OUTPUT_TEXT; + } final String label = getLabel(moreKeySpec); // Code is automatically generated for one letter label. - if (label != null && label.length() == 1) + if (label != null && label.length() == 1) { return label.charAt(0); - return Keyboard.CODE_UNSPECIFIED; + } + return Keyboard.CODE_OUTPUT_TEXT; } - public static int getIconId(String moreKeySpec) { + public static int getIconAttrId(String moreKeySpec) { if (hasIcon(moreKeySpec)) { - int end = moreKeySpec.indexOf(LABEL_END, PREFIX_ICON.length() + 1); - final String iconId = moreKeySpec.substring(PREFIX_ICON.length(), end); - try { - return Integer.valueOf(iconId); - } catch (NumberFormatException e) { - Log.w(TAG, "illegal icon id specified: " + iconId); - return KeyboardIconsSet.ICON_UNDEFINED; - } + final int end = moreKeySpec.indexOf(LABEL_END, PREFIX_ICON.length()); + final String name = moreKeySpec.substring(PREFIX_ICON.length(), end); + return KeyboardIconsSet.getIconAttrId(name); } return KeyboardIconsSet.ICON_UNDEFINED; } - private static int getResourceId(Resources res, String name) { - String packageName = res.getResourcePackageName(R.string.english_ime_name); - int resId = res.getIdentifier(name, null, packageName); - if (resId == 0) - throw new MoreKeySpecParserError("Unknown resource: " + name); - return resId; - } - @SuppressWarnings("serial") public static class MoreKeySpecParserError extends RuntimeException { public MoreKeySpecParserError(String message) { @@ -196,21 +191,19 @@ public class MoreKeySpecParser { } }; - public static CharSequence[] filterOut(Resources res, CharSequence[] moreKeys, - CodeFilter filter) { + public static String[] filterOut(Resources res, String[] moreKeys, CodeFilter filter) { if (moreKeys == null || moreKeys.length < 1) { return null; } - if (moreKeys.length == 1 - && filter.shouldFilterOut(getCode(res, moreKeys[0].toString()))) { + if (moreKeys.length == 1 && filter.shouldFilterOut(getCode(res, moreKeys[0]))) { return null; } - ArrayList<CharSequence> filtered = null; + ArrayList<String> filtered = null; for (int i = 0; i < moreKeys.length; i++) { - final CharSequence moreKeySpec = moreKeys[i]; - if (filter.shouldFilterOut(getCode(res, moreKeySpec.toString()))) { + final String moreKeySpec = moreKeys[i]; + if (filter.shouldFilterOut(getCode(res, moreKeySpec))) { if (filtered == null) { - filtered = new ArrayList<CharSequence>(); + filtered = new ArrayList<String>(); for (int j = 0; j < i; j++) { filtered.add(moreKeys[j]); } @@ -225,6 +218,6 @@ public class MoreKeySpecParser { if (filtered.size() == 0) { return null; } - return filtered.toArray(new CharSequence[filtered.size()]); + return filtered.toArray(new String[filtered.size()]); } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index d11aaeb96..94c47bdc9 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1881,6 +1881,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } return; } + // We need to log before we commit, because the word composer will store away the user + // typed word. + LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(), + suggestion.toString(), index, suggestions.mWords); mExpectingUpdateSelection = true; commitChosenWord(suggestion, WordComposer.COMMIT_TYPE_MANUAL_PICK); // Add the word to the auto dictionary if it's not a known word @@ -1890,10 +1894,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { addToOnlyBigramDictionary(suggestion, 1); } - // TODO: the following is fishy, because it seems there may be cases where we are not - // composing a word at all. Maybe throw an exception if !mWordComposer.isComposingWord() ? - LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(), - suggestion.toString(), index, suggestions.mWords); // Follow it with a space if (mInputAttributes.mInsertSpaceOnPickSuggestionManually) { sendMagicSpace(); @@ -1924,8 +1924,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Updating the predictions right away may be slow and feel unresponsive on slower // terminals. On the other hand if we just postUpdateBigramPredictions() it will // take a noticeable delay to update them which may feel uneasy. - } - if (showingAddToDictionaryHint) { + } else { if (mIsUserDictionaryAvailable) { mSuggestionsView.showAddToDictionaryHint( suggestion, mSettingsValues.mHintToSaveText); @@ -1942,9 +1941,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar * Commits the chosen word to the text field and saves it for later retrieval. */ private void commitChosenWord(final CharSequence bestWord, final int commitType) { - final KeyboardSwitcher switcher = mKeyboardSwitcher; - if (!switcher.isKeyboardAvailable()) - return; final InputConnection ic = getCurrentInputConnection(); if (ic != null) { mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators); @@ -2133,12 +2129,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final String wordBeforeCursor = ic.getTextBeforeCursor(cancelLength + 1, 0).subSequence(0, cancelLength) .toString(); - if (!autoCorrectedTo.equals(wordBeforeCursor)) { + if (!TextUtils.equals(autoCorrectedTo, wordBeforeCursor)) { throw new RuntimeException("cancelAutoCorrect check failed: we thought we were " + "reverting \"" + autoCorrectedTo + "\", but before the cursor we found \"" + wordBeforeCursor + "\""); } - if (originallyTypedWord.equals(wordBeforeCursor)) { + if (TextUtils.equals(originallyTypedWord, wordBeforeCursor)) { throw new RuntimeException("cancelAutoCorrect check failed: we wanted to cancel " + "auto correction and revert to \"" + originallyTypedWord + "\" but we found this very string before the cursor"); @@ -2158,12 +2154,22 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // "ic" must not be null private void restartSuggestionsOnManuallyPickedTypedWord(final InputConnection ic) { + // Note: this relies on the last word still being held in the WordComposer, in + // the field for suggestion resuming. + // Note: in the interest of code simplicity, we may want to just call + // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving + // the old WordComposer allows to reuse the actual typed coordinates. + mWordComposer.resumeSuggestionOnKeptWord(); + // We resume suggestion, and then we want to set the composing text to the content + // of the word composer again. But since we just manually picked a word, there is + // no composing text at the moment, so we have to delete the word before we set a + // new composing text. final int restartLength = mWordComposer.size(); if (DEBUG) { final String wordBeforeCursor = ic.getTextBeforeCursor(restartLength + 1, 0).subSequence(0, restartLength) .toString(); - if (!mWordComposer.getTypedWord().equals(wordBeforeCursor)) { + if (!TextUtils.equals(mWordComposer.getTypedWord(), wordBeforeCursor)) { throw new RuntimeException("restartSuggestionsOnManuallyPickedTypedWord " + "check failed: we thought we were reverting \"" + mWordComposer.getTypedWord() @@ -2171,13 +2177,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar + wordBeforeCursor + "\""); } } + // Warning: this +1 takes into account the extra space added by the manual pick process. ic.deleteSurroundingText(restartLength + 1, 0); - - // Note: this relies on the last word still being held in the WordComposer - // Note: in the interest of code simplicity, we may want to just call - // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving - // the old WordComposer allows to reuse the actual typed coordinates. - mWordComposer.resumeSuggestionOnKeptWord(); ic.setComposingText(mWordComposer.getTypedWord(), 1); mHandler.cancelUpdateBigramPredictions(); mHandler.postUpdateSuggestions(); diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index 8e0cfa122..bc8a1301c 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -16,14 +16,6 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.compat.InputMethodInfoCompatWrapper; -import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; -import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper; -import com.android.inputmethod.compat.InputTypeCompatUtils; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.latin.define.JniLibName; - import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -41,6 +33,14 @@ import android.text.format.DateUtils; import android.util.Log; import android.view.inputmethod.EditorInfo; +import com.android.inputmethod.compat.InputMethodInfoCompatWrapper; +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper; +import com.android.inputmethod.compat.InputTypeCompatUtils; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.latin.define.JniLibName; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -62,6 +62,12 @@ public class Utils { private static boolean DBG = LatinImeLogger.sDBG; private static boolean DBG_EDIT_DISTANCE = false; + // Constants for resource name parsing. + public static final char ESCAPE_CHAR = '\\'; + public static final char PREFIX_AT = '@'; + public static final char SUFFIX_SLASH = '/'; + private static final String PREFIX_STRING = PREFIX_AT + "string"; + private Utils() { // Intentional empty constructor for utility class. } @@ -793,4 +799,62 @@ public class Utils { LatinImeLogger.logOnAutoCorrectionCancelled(); } } + + public static int getResourceId(Resources res, String name, int packageNameResId) { + String packageName = res.getResourcePackageName(packageNameResId); + int resId = res.getIdentifier(name, null, packageName); + if (resId == 0) { + throw new RuntimeException("Unknown resource: " + name); + } + return resId; + } + + public static String resolveStringResource(String text, Resources res, int packageNameResId) { + final int size = text.length(); + if (size < PREFIX_STRING.length()) { + return text; + } + + StringBuilder sb = null; + for (int pos = 0; pos < size; pos++) { + final char c = text.charAt(pos); + if (c == PREFIX_AT && text.startsWith(PREFIX_STRING, pos)) { + if (sb == null) { + sb = new StringBuilder(text.substring(0, pos)); + } + final int end = Utils.searchResourceNameEnd(text, pos + PREFIX_STRING.length()); + final String resName = text.substring(pos + 1, end); + final int resId = getResourceId(res, resName, packageNameResId); + sb.append(res.getString(resId)); + pos = end - 1; + } else if (c == ESCAPE_CHAR) { + pos++; + if (sb != null) { + sb.append(c); + if (pos < size) { + sb.append(text.charAt(pos)); + } + } + } else if (sb != null) { + sb.append(c); + } + } + return (sb == null) ? text : sb.toString(); + } + + private static int searchResourceNameEnd(String text, int start) { + final int size = text.length(); + if (start >= size || text.charAt(start) != SUFFIX_SLASH) { + throw new RuntimeException("Resource name not specified"); + } + for (int pos = start + 1; pos < size; pos++) { + final char c = text.charAt(pos); + // String resource name should be consisted of [a-z_0-9]. + if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) { + continue; + } + return pos; + } + return size; + } } diff --git a/native/src/correction.cpp b/native/src/correction.cpp index 6a129d4e3..63dd283c8 100644 --- a/native/src/correction.cpp +++ b/native/src/correction.cpp @@ -39,15 +39,15 @@ inline static void initEditDistance(int *editDistanceTable) { } } -inline static void dumpEditDistance10ForDebug(int *editDistanceTable, const int inputLength, - const int outputLength) { +inline static void dumpEditDistance10ForDebug(int *editDistanceTable, + const int editDistanceTableWidth, const int outputLength) { if (DEBUG_DICT) { AKLOGI("EditDistanceTable"); for (int i = 0; i <= 10; ++i) { int c[11]; for (int j = 0; j <= 10; ++j) { - if (j < inputLength + 1 && i < outputLength + 1) { - c[j] = (editDistanceTable + i * (inputLength + 1))[j]; + if (j < editDistanceTableWidth + 1 && i < outputLength + 1) { + c[j] = (editDistanceTable + i * (editDistanceTableWidth + 1))[j]; } else { c[j] = -1; } @@ -81,12 +81,12 @@ inline static void calcEditDistanceOneStep(int *editDistanceTable, const unsigne } } -inline static int getCurrentEditDistance( - int *editDistanceTable, const int inputLength, const int outputLength) { +inline static int getCurrentEditDistance(int *editDistanceTable, const int editDistanceTableWidth, + const int outputLength, const int inputLength) { if (DEBUG_EDIT_DISTANCE) { AKLOGI("getCurrentEditDistance %d, %d", inputLength, outputLength); } - return editDistanceTable[(inputLength + 1) * (outputLength + 1) - 1]; + return editDistanceTable[(editDistanceTableWidth + 1) * (outputLength) + inputLength]; } ////////////////////// @@ -165,6 +165,16 @@ int Correction::getFreqForSplitTwoWords(const int firstFreq, const int secondFre } int Correction::getFinalFreq(const int freq, unsigned short **word, int *wordLength) { + return getFinalFreqInternal(freq, word, wordLength, mInputLength); +} + +int Correction::getFinalFreqForSubQueue(const int freq, unsigned short **word, int *wordLength, + const int inputLength) { + return getFinalFreqInternal(freq, word, wordLength, inputLength); +} + +int Correction::getFinalFreqInternal(const int freq, unsigned short **word, int *wordLength, + const int inputLength) { const int outputIndex = mTerminalOutputIndex; const int inputIndex = mTerminalInputIndex; *wordLength = outputIndex + 1; @@ -173,8 +183,9 @@ int Correction::getFinalFreq(const int freq, unsigned short **word, int *wordLen } *word = mWord; - return Correction::RankingAlgorithm::calculateFinalFreq( - inputIndex, outputIndex, freq, mEditDistanceTable, this); + int finalFreq = Correction::RankingAlgorithm::calculateFinalFreq( + inputIndex, outputIndex, freq, mEditDistanceTable, this, inputLength); + return finalFreq; } bool Correction::initProcessState(const int outputIndex) { @@ -613,9 +624,9 @@ inline static bool isUpperCase(unsigned short c) { /* static */ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const int outputIndex, - const int freq, int* editDistanceTable, const Correction* correction) { + const int freq, int* editDistanceTable, const Correction* correction, + const int inputLength) { const int excessivePos = correction->getExcessivePos(); - const int inputLength = correction->mInputLength; const int typedLetterMultiplier = correction->TYPED_LETTER_MULTIPLIER; const int fullWordMultiplier = correction->FULL_WORD_MULTIPLIER; const ProximityInfo *proximityInfo = correction->mProximityInfo; @@ -640,13 +651,13 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const const unsigned short* word = correction->mWord; const bool skipped = skippedCount > 0; - const int quoteDiffCount = max(0, getQuoteCount(word, outputIndex + 1) + const int quoteDiffCount = max(0, getQuoteCount(word, outputLength) - getQuoteCount(proximityInfo->getPrimaryInputWord(), inputLength)); // TODO: Calculate edit distance for transposed and excessive int ed = 0; if (DEBUG_DICT_FULL) { - dumpEditDistance10ForDebug(editDistanceTable, inputLength, outputIndex + 1); + dumpEditDistance10ForDebug(editDistanceTable, correction->mInputLength, outputLength); } int adjustedProximityMatchedCount = proximityMatchedCount; @@ -654,22 +665,22 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const // TODO: Optimize this. if (transposedCount > 0 || proximityMatchedCount > 0 || skipped || excessiveCount > 0) { - ed = getCurrentEditDistance(editDistanceTable, inputLength, outputIndex + 1) - - transposedCount; + ed = getCurrentEditDistance(editDistanceTable, correction->mInputLength, outputLength, + inputLength) - transposedCount; const int matchWeight = powerIntCapped(typedLetterMultiplier, - max(inputLength, outputIndex + 1) - ed); + max(inputLength, outputLength) - ed); multiplyIntCapped(matchWeight, &finalFreq); // TODO: Demote further if there are two or more excessive chars with longer user input? - if (inputLength > outputIndex + 1) { + if (inputLength > outputLength) { multiplyRate(INPUT_EXCEEDS_OUTPUT_DEMOTION_RATE, &finalFreq); } ed = max(0, ed - quoteDiffCount); if (transposedCount < 1) { - if (ed == 1 && (inputLength == outputIndex || inputLength == outputIndex + 2)) { + if (ed == 1 && (inputLength == outputLength - 1 || inputLength == outputLength + 1)) { // Promote a word with just one skipped or excessive char if (sameLength) { multiplyRate(WORDS_WITH_JUST_ONE_CORRECTION_PROMOTION_RATE, &finalFreq); @@ -681,7 +692,7 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const sameLength = true; } } - adjustedProximityMatchedCount = min(max(0, ed - (outputIndex + 1 - inputLength)), + adjustedProximityMatchedCount = min(max(0, ed - (outputLength - inputLength)), proximityMatchedCount); } else { const int matchWeight = powerIntCapped(typedLetterMultiplier, matchCount); @@ -783,7 +794,8 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const // Promotion for an exactly matched word if (ed == 0) { // Full exact match - if (sameLength && transposedCount == 0 && !skipped && excessiveCount == 0) { + if (sameLength && transposedCount == 0 && !skipped && excessiveCount == 0 + && quoteDiffCount == 0) { finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq); } } @@ -828,14 +840,14 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const } if (DEBUG_DICT_FULL) { - AKLOGI("calc: %d, %d", outputIndex, sameLength); + AKLOGI("calc: %d, %d", outputLength, sameLength); } if (DEBUG_CORRECTION_FREQ) { - DUMP_WORD(correction->mWord, outputIndex + 1); - AKLOGI("FinalFreq: [P%d, S%d, T%d, E%d] %d, %d, %d, %d, %d", proximityMatchedCount, - skippedCount, transposedCount, excessiveCount, lastCharExceeded, sameLength, - quoteDiffCount, ed, finalFreq); + DUMP_WORD(correction->mWord, outputLength); + AKLOGI("FinalFreq: [P%d, S%d, T%d, E%d] %d, %d, %d, %d, %d, %d", proximityMatchedCount, + skippedCount, transposedCount, excessiveCount, outputLength, lastCharExceeded, + sameLength, quoteDiffCount, ed, finalFreq); } return finalFreq; @@ -881,11 +893,12 @@ int Correction::RankingAlgorithm::calcFreqForSplitTwoWords( if (firstWordLength == 0 || secondWordLength == 0) { return 0; } - const int firstDemotionRate = 100 - 100 / (firstWordLength + 1); + const int firstDemotionRate = 100 - TWO_WORDS_CORRECTION_DEMOTION_BASE / (firstWordLength + 1); int tempFirstFreq = firstFreq; multiplyRate(firstDemotionRate, &tempFirstFreq); - const int secondDemotionRate = 100 - 100 / (secondWordLength + 1); + const int secondDemotionRate = 100 + - TWO_WORDS_CORRECTION_DEMOTION_BASE / (secondWordLength + 1); int tempSecondFreq = secondFreq; multiplyRate(secondDemotionRate, &tempSecondFreq); diff --git a/native/src/correction.h b/native/src/correction.h index 22a424f5c..0715551d0 100644 --- a/native/src/correction.h +++ b/native/src/correction.h @@ -75,6 +75,8 @@ class Correction { int getFreqForSplitTwoWords( const int firstFreq, const int secondFreq, const unsigned short *word); int getFinalFreq(const int freq, unsigned short **word, int* wordLength); + int getFinalFreqForSubQueue(const int freq, unsigned short **word, int* wordLength, + const int inputLength); CorrectionType processCharAndCalcState(const int32_t c, const bool isTerminal); @@ -97,7 +99,8 @@ class Correction { class RankingAlgorithm { public: static int calculateFinalFreq(const int inputIndex, const int depth, - const int freq, int *editDistanceTable, const Correction* correction); + const int freq, int *editDistanceTable, const Correction* correction, + const int inputLength); static int calcFreqForSplitTwoWords(const int firstFreq, const int secondFreq, const Correction* correction, const unsigned short *word); static int calcFreqForSplitTwoWordsOld(const int firstFreq, const int secondFreq, @@ -122,6 +125,8 @@ class Correction { const int32_t c, const bool isTerminal, const bool inputIndexIncremented); inline CorrectionType processUnrelatedCorrectionType(); inline void addCharToCurrentWord(const int32_t c); + inline int getFinalFreqInternal(const int freq, unsigned short **word, int* wordLength, + const int inputLength); const int TYPED_LETTER_MULTIPLIER; const int FULL_WORD_MULTIPLIER; diff --git a/native/src/defines.h b/native/src/defines.h index d739043a4..119a7d779 100644 --- a/native/src/defines.h +++ b/native/src/defines.h @@ -187,7 +187,7 @@ static void prof_out(void) { // The following "rate"s are used as a multiplier before dividing by 100, so they are in percent. #define WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE 80 #define WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X 12 -#define WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE 67 +#define WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE 58 #define WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE 75 #define WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE 75 #define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 70 @@ -199,6 +199,8 @@ static void prof_out(void) { #define INPUT_EXCEEDS_OUTPUT_DEMOTION_RATE 70 #define FIRST_CHAR_DIFFERENT_DEMOTION_RATE 96 #define TWO_WORDS_CAPITALIZED_DEMOTION_RATE 50 +#define TWO_WORDS_CORRECTION_DEMOTION_BASE 80 +#define TWO_WORDS_PLUS_OTHER_ERROR_CORRECTION_DEMOTION_DIVIDER 1 #define ZERO_DISTANCE_PROMOTION_RATE 110 #define NEUTRAL_SCORE_SQUARED_RADIUS 8.0f #define HALF_SCORE_SQUARED_RADIUS 32.0f @@ -212,8 +214,10 @@ static void prof_out(void) { // Holds up to 1 candidate for each word #define SUB_QUEUE_MAX_WORDS 1 #define SUB_QUEUE_MAX_COUNT 10 +#define SUB_QUEUE_MIN_WORD_LENGTH 4 -#define TWO_WORDS_CORRECTION_THRESHOLD 0.22f +#define TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD 0.39 +#define START_TWO_WORDS_CORRECTION_THRESHOLD 0.22 #define MAX_DEPTH_MULTIPLIER 3 diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp index 8be95bc40..2c5b9402a 100644 --- a/native/src/unigram_dictionary.cpp +++ b/native/src/unigram_dictionary.cpp @@ -254,7 +254,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, proximityInfo->getPrimaryInputWord(), i, word, wordLength, score); ns += 0; AKLOGI("--- TOP SUB WORDS for %d --- %d %f [%d]", i, score, ns, - (ns > TWO_WORDS_CORRECTION_THRESHOLD)); + (ns > TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD)); DUMP_WORD(proximityInfo->getPrimaryInputWord(), i); DUMP_WORD(word, wordLength); } @@ -343,43 +343,45 @@ inline void UnigramDictionary::onTerminal(const int freq, WordsPriorityQueuePool *queuePool, const bool addToMasterQueue) { const int inputIndex = correction->getInputIndex(); const bool addToSubQueue = inputIndex < SUB_QUEUE_MAX_COUNT; - if (!addToMasterQueue && !addToSubQueue) { - return; - } - WordsPriorityQueue *masterQueue = queuePool->getMasterQueue(); - WordsPriorityQueue *subQueue = queuePool->getSubQueue1(inputIndex); + int wordLength; unsigned short* wordPointer; - const int finalFreq = correction->getFinalFreq(freq, &wordPointer, &wordLength); - if (finalFreq != NOT_A_FREQUENCY) { - if (!terminalAttributes.isShortcutOnly()) { - if (addToMasterQueue) { + + if (addToMasterQueue) { + WordsPriorityQueue *masterQueue = queuePool->getMasterQueue(); + const int finalFreq = correction->getFinalFreq(freq, &wordPointer, &wordLength); + if (finalFreq != NOT_A_FREQUENCY) { + if (!terminalAttributes.isShortcutOnly()) { addWord(wordPointer, wordLength, finalFreq, masterQueue); } - // TODO: Check the validity of "inputIndex == wordLength" - //if (addToSubQueue && inputIndex == wordLength) { - if (addToSubQueue) { - addWord(wordPointer, wordLength, finalFreq, subQueue); + + // Please note that the shortcut candidates will be added to the master queue only. + TerminalAttributes::ShortcutIterator iterator = + terminalAttributes.getShortcutIterator(); + while (iterator.hasNextShortcutTarget()) { + // TODO: addWord only supports weak ordering, meaning we have no means + // to control the order of the shortcuts relative to one another or to the word. + // We need to either modulate the frequency of each shortcut according + // to its own shortcut frequency or to make the queue + // so that the insert order is protected inside the queue for words + // with the same score. + uint16_t shortcutTarget[MAX_WORD_LENGTH_INTERNAL]; + const int shortcutTargetStringLength = iterator.getNextShortcutTarget( + MAX_WORD_LENGTH_INTERNAL, shortcutTarget); + addWord(shortcutTarget, shortcutTargetStringLength, finalFreq, masterQueue); } } - // Please note that the shortcut candidates will be added to the master queue only. - if (!addToMasterQueue) { - return; - } + } - // From here, below is the code to add shortcut candidates. - TerminalAttributes::ShortcutIterator iterator = terminalAttributes.getShortcutIterator(); - while (iterator.hasNextShortcutTarget()) { - // TODO: addWord only supports weak ordering, meaning we have no means to control the - // order of the shortcuts relative to one another or to the word. We need to either - // modulate the frequency of each shortcut according to its own shortcut frequency or - // to make the queue so that the insert order is protected inside the queue for words - // with the same score. - uint16_t shortcutTarget[MAX_WORD_LENGTH_INTERNAL]; - const int shortcutTargetStringLength = iterator.getNextShortcutTarget( - MAX_WORD_LENGTH_INTERNAL, shortcutTarget); - addWord(shortcutTarget, shortcutTargetStringLength, finalFreq, masterQueue); - } + // We only allow two words + other error correction for words with SUB_QUEUE_MIN_WORD_LENGTH + // or more length. + if (inputIndex >= SUB_QUEUE_MIN_WORD_LENGTH && addToSubQueue) { + // TODO: Check the validity of "inputIndex == wordLength" + //if (addToSubQueue && inputIndex == wordLength) { + WordsPriorityQueue *subQueue = queuePool->getSubQueue1(inputIndex); + const int finalFreq = correction->getFinalFreqForSubQueue(freq, &wordPointer, &wordLength, + inputIndex); + addWord(wordPointer, wordLength, finalFreq, subQueue); } } @@ -397,20 +399,57 @@ void UnigramDictionary::getSplitTwoWordsSuggestions(ProximityInfo *proximityInfo } const bool isSpaceProximity = spaceProximityPos >= 0; const int firstWordStartPos = 0; + + const int firstTypedWordLength = isSpaceProximity ? spaceProximityPos : missingSpacePos; + int firstFreq = getMostFrequentWordLike(0, firstTypedWordLength, proximityInfo, mWord); + unsigned short* firstWord = 0; + int firstWordLength = 0; + if (firstFreq > 0) { + firstWordLength = firstTypedWordLength; + firstWord = mWord; + } else { + if (masterQueue->size() > 0) { + double nsForMaster = masterQueue->getHighestNormalizedScore( + proximityInfo->getPrimaryInputWord(), inputLength, 0, 0, 0); + if (nsForMaster > START_TWO_WORDS_CORRECTION_THRESHOLD) { + // Do nothing if the highest suggestion exceeds the threshold. + return; + } + } + WordsPriorityQueue* firstWordQueue = queuePool->getSubQueue1(firstTypedWordLength); + if (firstWordQueue->size() < 1) { + return; + } + int score = 0; + const double ns = firstWordQueue->getHighestNormalizedScore( + proximityInfo->getPrimaryInputWord(), firstTypedWordLength, &firstWord, &score, + &firstWordLength); + // Two words correction won't be done if the score of the first word doesn't exceed the + // threshold. + if (ns < TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD) { + return; + } + firstFreq = score >> (firstWordLength + + TWO_WORDS_PLUS_OTHER_ERROR_CORRECTION_DEMOTION_DIVIDER); + } + + if (firstFreq <= 0) { + return; + } + const int secondWordStartPos = isSpaceProximity ? (spaceProximityPos + 1) : missingSpacePos; - const int firstWordLength = isSpaceProximity ? spaceProximityPos : missingSpacePos; const int secondWordLength = isSpaceProximity ? (inputLength - spaceProximityPos - 1) : (inputLength - missingSpacePos); if (inputLength >= MAX_WORD_LENGTH) return; + if (0 >= firstWordLength || 0 >= secondWordLength || firstWordStartPos >= secondWordStartPos || firstWordStartPos < 0 || secondWordStartPos + secondWordLength > inputLength) return; const int newWordLength = firstWordLength + secondWordLength + 1; - // Space proximity preparation //WordsPriorityQueue *subQueue = queuePool->getSubQueue1(); //initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, firstWordLength, subQueue, @@ -420,15 +459,12 @@ void UnigramDictionary::getSplitTwoWordsSuggestions(ProximityInfo *proximityInfo // Allocating variable length array on stack unsigned short word[newWordLength]; - const int firstFreq = getMostFrequentWordLike( - firstWordStartPos, firstWordLength, proximityInfo, mWord); if (DEBUG_DICT) { AKLOGI("First freq: %d", firstFreq); } - if (firstFreq <= 0) return; for (int i = 0; i < firstWordLength; ++i) { - word[i] = mWord[i]; + word[i] = firstWord[i]; } const int secondFreq = getMostFrequentWordLike( diff --git a/native/src/words_priority_queue.h b/native/src/words_priority_queue.h index 6262439b5..c85f2b9b3 100644 --- a/native/src/words_priority_queue.h +++ b/native/src/words_priority_queue.h @@ -47,6 +47,7 @@ class WordsPriorityQueue { for (int i = 0; i < maxWordLength; ++i) { mSuggestedWords[i].mUsed = false; } + mHighestSuggestedWord = 0; } ~WordsPriorityQueue() { @@ -79,6 +80,9 @@ class WordsPriorityQueue { DUMP_WORD(word, wordLength); } mSuggestions.push(sw); + if (!mHighestSuggestedWord || mHighestSuggestedWord->mScore < sw->mScore) { + mHighestSuggestedWord = sw; + } } SuggestedWord* top() { @@ -88,6 +92,7 @@ class WordsPriorityQueue { } int outputSuggestions(int *frequencies, unsigned short *outputChars) { + mHighestSuggestedWord = 0; const unsigned int size = min(MAX_WORDS, mSuggestions.size()); int index = size - 1; while (!mSuggestions.empty() && index >= 0) { @@ -116,6 +121,7 @@ class WordsPriorityQueue { } void clear() { + mHighestSuggestedWord = 0; while (!mSuggestions.empty()) { SuggestedWord* sw = mSuggestions.top(); if (DEBUG_WORDS_PRIORITY_QUEUE) { @@ -134,6 +140,28 @@ class WordsPriorityQueue { DUMP_WORD(mSuggestions.top()->mWord, mSuggestions.top()->mWordLength); } + double getHighestNormalizedScore(const unsigned short* before, const int beforeLength, + unsigned short** outWord, int *outScore, int *outLength) { + if (!mHighestSuggestedWord) { + return 0.0; + } + SuggestedWord* sw = mHighestSuggestedWord; + const int score = sw->mScore; + unsigned short* word = sw->mWord; + const int wordLength = sw->mWordLength; + if (outScore) { + *outScore = score; + } + if (outWord) { + *outWord = word; + } + if (outLength) { + *outLength = wordLength; + } + return Correction::RankingAlgorithm::calcNormalizedScore( + before, beforeLength, word, wordLength, score); + } + private: struct wordComparator { bool operator ()(SuggestedWord * left, SuggestedWord * right) { @@ -158,6 +186,7 @@ class WordsPriorityQueue { const unsigned int MAX_WORDS; const unsigned int MAX_WORD_LENGTH; SuggestedWord* mSuggestedWords; + SuggestedWord* mHighestSuggestedWord; }; } diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml new file mode 100644 index 000000000..bfd1c1716 --- /dev/null +++ b/tests/res/values/strings.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources> + <string name="empty_string">""</string> + <string name="single_char">"a"</string> + <string name="space">" "</string> + <string name="single_label">"abc"</string> + <string name="spaces">" "</string> + <string name="spaces_in_label">"a b c"</string> + <string name="spaces_at_beginning_of_label">" abc"</string> + <string name="spaces_at_end_of_label">"abc "</string> + <string name="label_surrounded_by_spaces">" abc "</string> + <string name="escaped_char">"\\a"</string> + <string name="escaped_comma">"\\,"</string> + <string name="escaped_escape">"\\\\"</string> + <string name="escaped_label">"a\\bc"</string> + <string name="escaped_label_at_beginning">"\\abc"</string> + <string name="escaped_label_with_comma">"a\\,c"</string> + <string name="escaped_label_with_comma_at_beginning">"\\,bc"</string> + <string name="escaped_label_with_successive">"\\,\\\\bc"</string> + <string name="escaped_label_with_escape">"a\\\\c"</string> + <string name="multiple_chars">"a,b,c"</string> + <string name="multiple_chars_surrounded_by_spaces">" a , b , c "</string> + <string name="multiple_labels">"abc,def,ghi"</string> + <string name="multiple_labels_surrounded_by_spaces">" abc , def , ghi "</string> + <string name="multiple_chars_with_comma">"a,\\,,c"</string> + <string name="multiple_chars_with_comma_surrounded_by_spaces">" a , \\, , c "</string> + <string name="multiple_labels_with_escape">"\\abc,d\\ef,gh\\i"</string> + <string name="multiple_labels_with_escape_surrounded_by_spaces">" \\abc , d\\ef , gh\\i "</string> + <string name="multiple_labels_with_comma_and_escape">"ab\\\\,d\\\\\\,,g\\,i"</string> + <string name="multiple_labels_with_comma_and_escape_surrounded_by_spaces">" ab\\\\ , d\\\\\\, , g\\,i "</string> +</resources> diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java index 4050a7123..29881d91c 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyStylesTests.java @@ -16,30 +16,53 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.keyboard.internal.KeyStyles.EmptyKeyStyle; - +import android.content.res.Resources; import android.test.AndroidTestCase; import android.text.TextUtils; +import com.android.inputmethod.latin.tests.R; + +import java.util.Arrays; + public class KeyStylesTests extends AndroidTestCase { + private Resources mTestResources; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mTestResources = getTestContext().getResources(); + } + private static String format(String message, Object expected, Object actual) { return message + " expected:<" + expected + "> but was:<" + actual + ">"; } - private static void assertTextArray(String message, CharSequence value, - CharSequence ... expected) { - final CharSequence actual[] = EmptyKeyStyle.parseCsvText(value); + private void assertTextArray(String message, String value, String ... expected) { + final String actual[] = KeyStyles.parseCsvText(value, mTestResources, + R.string.empty_string); if (expected.length == 0) { assertNull(message, actual); return; } - assertSame(message + ": result length", expected.length, actual.length); + assertEquals(message + ": expected=" + Arrays.toString(expected) + + " actual=" + Arrays.toString(actual) + + ": result length", expected.length, actual.length); for (int i = 0; i < actual.length; i++) { final boolean equals = TextUtils.equals(expected[i], actual[i]); assertTrue(format(message + ": result at " + i + ":", expected[i], actual[i]), equals); } } + private void assertError(String message, String value, String ... expected) { + try { + assertTextArray(message, value, expected); + fail(message); + } catch (Exception pcpe) { + // success. + } + } + public void testParseCsvTextZero() { assertTextArray("Empty string", ""); } @@ -52,7 +75,10 @@ public class KeyStylesTests extends AndroidTestCase { assertTextArray("Spaces in label", "a b c", "a b c"); assertTextArray("Spaces at beginning of label", " abc", " abc"); assertTextArray("Spaces at end of label", "abc ", "abc "); - assertTextArray("label surrounded by spaces", " abc ", " abc "); + assertTextArray("Label surrounded by spaces", " abc ", " abc "); + + assertTextArray("Incomplete resource reference 1", "string", "string"); + assertTextArray("Incomplete resource reference 2", "@strin", "@strin"); } public void testParseCsvTextSingleEscaped() { @@ -60,11 +86,13 @@ public class KeyStylesTests extends AndroidTestCase { assertTextArray("Escaped comma", "\\,", ","); assertTextArray("Escaped escape", "\\\\", "\\"); assertTextArray("Escaped label", "a\\bc", "abc"); - assertTextArray("Escaped label at begininng", "\\abc", "abc"); + assertTextArray("Escaped label at beginning", "\\abc", "abc"); assertTextArray("Escaped label with comma", "a\\,c", "a,c"); assertTextArray("Escaped label with comma at beginning", "\\,bc", ",bc"); assertTextArray("Escaped label with successive", "\\,\\\\bc", ",\\bc"); assertTextArray("Escaped label with escape", "a\\\\c", "a\\c"); + + assertTextArray("Escaped @string", "\\@string/empty_string", "@string/empty_string"); } public void testParseCsvTextMulti() { @@ -86,5 +114,109 @@ public class KeyStylesTests extends AndroidTestCase { "ab\\\\,d\\\\\\,,g\\,i", "ab\\", "d\\,", "g,i"); assertTextArray("Multiple labels with comma and escape surrounded by spaces", " ab\\\\ , d\\\\\\, , g\\,i ", " ab\\ ", " d\\, ", " g,i "); + + assertTextArray("Multiple escaped @string", "\\@,\\@string/empty_string", + "@", "@string/empty_string"); + } + + public void testParseCsvResourceError() { + assertError("Incomplete resource name 1", "@string", "@string"); + assertError("Incomplete resource name 2", "@string/", "@string/"); + assertError("Non existing resource", "@string/non_existing"); + } + + public void testParseCsvResourceZero() { + assertTextArray("Empty string", + "@string/empty_string"); + } + + public void testParseCsvResourceSingle() { + assertTextArray("Single char", + "@string/single_char", "a"); + assertTextArray("Space", + "@string/space", " "); + assertTextArray("Single label", + "@string/single_label", "abc"); + assertTextArray("Spaces", + "@string/spaces", " "); + assertTextArray("Spaces in label", + "@string/spaces_in_label", "a b c"); + assertTextArray("Spaces at beginning of label", + "@string/spaces_at_beginning_of_label", " abc"); + assertTextArray("Spaces at end of label", + "@string/spaces_at_end_of_label", "abc "); + assertTextArray("label surrounded by spaces", + "@string/label_surrounded_by_spaces", " abc "); + } + + public void testParseCsvResourceSingleEscaped() { + assertTextArray("Escaped char", + "@string/escaped_char", "a"); + assertTextArray("Escaped comma", + "@string/escaped_comma", ","); + assertTextArray("Escaped escape", + "@string/escaped_escape", "\\"); + assertTextArray("Escaped label", + "@string/escaped_label", "abc"); + assertTextArray("Escaped label at beginning", + "@string/escaped_label_at_beginning", "abc"); + assertTextArray("Escaped label with comma", + "@string/escaped_label_with_comma", "a,c"); + assertTextArray("Escaped label with comma at beginning", + "@string/escaped_label_with_comma_at_beginning", ",bc"); + assertTextArray("Escaped label with successive", + "@string/escaped_label_with_successive", ",\\bc"); + assertTextArray("Escaped label with escape", + "@string/escaped_label_with_escape", "a\\c"); + } + + public void testParseCsvResourceMulti() { + assertTextArray("Multiple chars", + "@string/multiple_chars", "a", "b", "c"); + assertTextArray("Multiple chars surrounded by spaces", + "@string/multiple_chars_surrounded_by_spaces", + " a ", " b ", " c "); + assertTextArray("Multiple labels", + "@string/multiple_labels", "abc", "def", "ghi"); + assertTextArray("Multiple labels surrounded by spaces", + "@string/multiple_labels_surrounded_by_spaces", " abc ", " def ", " ghi "); + } + + public void testParseCsvResourcetMultiEscaped() { + assertTextArray("Multiple chars with comma", + "@string/multiple_chars_with_comma", + "a", ",", "c"); + assertTextArray("Multiple chars with comma surrounded by spaces", + "@string/multiple_chars_with_comma_surrounded_by_spaces", + " a ", " , ", " c "); + assertTextArray("Multiple labels with escape", + "@string/multiple_labels_with_escape", + "abc", "def", "ghi"); + assertTextArray("Multiple labels with escape surrounded by spaces", + "@string/multiple_labels_with_escape_surrounded_by_spaces", + " abc ", " def ", " ghi "); + assertTextArray("Multiple labels with comma and escape", + "@string/multiple_labels_with_comma_and_escape", + "ab\\", "d\\,", "g,i"); + assertTextArray("Multiple labels with comma and escape surrounded by spaces", + "@string/multiple_labels_with_comma_and_escape_surrounded_by_spaces", + " ab\\ ", " d\\, ", " g,i "); + } + + public void testParseMultipleResources() { + assertTextArray("Literals and resources", + "1,@string/multiple_chars,z", "1", "a", "b", "c", "z"); + assertTextArray("Multiple single resource chars and labels", + "@string/single_char,@string/single_label,@string/escaped_comma", + "a", "abc", ","); + assertTextArray("Multiple multiple resource chars and labels", + "@string/multiple_chars,@string/multiple_labels,@string/multiple_chars_with_comma", + "a", "b", "c", "abc", "def", "ghi", "a", ",", "c"); + assertTextArray("Concatenated resources", + "@string/multiple_chars@string/multiple_labels@string/multiple_chars_with_comma", + "a", "b", "cabc", "def", "ghia", ",", "c"); + assertTextArray("Concatenated resource and literal", + "abc@string/multiple_labels", + "abcabc", "def", "ghi"); } } diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java new file mode 100644 index 000000000..729120bba --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.keyboard.internal; + +public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase { + // Shift key chording input. + public void testChording() { + // Press shift key and hold, enter into choring shift state. + pressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED); + + // Press/release letter keys. + chordingPressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + chordingPressAndReleaseKey('X', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + + // Release shift key, switch back to alphabet. + releaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED); + + // Press symbols key and hold, enter into choring symbols state. + pressKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); + + // Press/release symbol letter keys. + chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + chordingPressAndReleaseKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Release symbols key, switch back to alphabet. + releaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); + } + + // Shift key chording input in shift locked. + public void testShiftChordingShiftLocked() { + // Long press shift key, enter alphabet shift locked. + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); + + // Press shift key and hold, enter into choring shift state. + pressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED); + + // Press/release letter keys. + chordingPressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + chordingPressAndReleaseKey('X', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + + // Release shift key, switch back to alphabet shift locked. + releaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCKED); + + // Press symbols key and hold, enter into choring symbols state. + pressKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); + + // Press/release symbol letter keys. + chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + chordingPressAndReleaseKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Release symbols key, switch back to alphabet shift locked. + releaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED); + } + + // Symbols key chording input. + public void testSymbolsChording() { + // Press/release symbols key, enter symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Press shift key and hold, enter into choring symbols shifted state. + pressKey(CODE_SHIFT, SYMBOLS_SHIFTED); + + // Press/release symbols keys. + chordingPressAndReleaseKey('1', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + chordingPressAndReleaseKey('2', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + + // Release shift key, switch back to symbols. + releaseKey(CODE_SHIFT, SYMBOLS_UNSHIFTED); + + // Press "ABC" key and hold, enter into choring alphabet state. + pressKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); + + // Press/release letter keys. + chordingPressAndReleaseKey('a', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + chordingPressAndReleaseKey('b', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + + // Release "ABC" key, switch back to symbols. + releaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); + } + + // Symbols shifted key chording input in symbol. + public void testSymbolsShiftedChording() { + // Press/release symbols key, enter symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release shift key, enter symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + + // Press shift key and hold, enter into chording symbols state. + pressKey(CODE_SHIFT, SYMBOLS_UNSHIFTED); + + // Press/release symbol letter keys. + chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + chordingPressAndReleaseKey('2', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Release shift key, switch back to symbols shifted state. + releaseKey(CODE_SHIFT, SYMBOLS_SHIFTED); + + // Press "ABC" key and hold, enter into choring alphabet state. + pressKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); + + // Press/release letter keys. + chordingPressAndReleaseKey('a', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + chordingPressAndReleaseKey('b', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + + // Release "ABC" key, switch back to symbols. + releaseKey(CODE_SYMBOL, SYMBOLS_SHIFTED); + } + + // Chording shift key in automatic upper case. + public void testAutomaticUpperCaseChording() { + // Set auto caps mode on. + setAutoCapsMode(AUTO_CAPS); + + // Update shift state with auto caps enabled. + updateShiftState(ALPHABET_AUTOMATIC_SHIFTED); + + // Press shift key and hold, enter into chording shift state. + pressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED); + + // Press/release letter keys. + chordingPressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + + // Release shift key, switch back to alphabet. + releaseKey(CODE_SHIFT, ALPHABET_UNSHIFTED); + } + + // Chording symbol key in automatic upper case. + public void testAutomaticUpperCaseChording2() { + // Set auto caps mode on. + setAutoCapsMode(AUTO_CAPS); + + // Update shift state with auto caps enabled. + updateShiftState(ALPHABET_AUTOMATIC_SHIFTED); + + // Press "123?" key and hold, enter into chording symbols state. + pressKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED); + + // Press/release symbol letter keys. + chordingPressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Release "123?" key, switch back to alphabet. + releaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED); + } + + // TODO: Multitouch test + + // TODO: n-Keys roll over test +} diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateNonDistinctTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateNonDistinctTests.java deleted file mode 100644 index e3f0e0718..000000000 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateNonDistinctTests.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.keyboard.internal; - -import android.test.AndroidTestCase; - -public class KeyboardStateNonDistinctTests extends AndroidTestCase - implements MockKeyboardSwitcher.Constants { - protected MockKeyboardSwitcher mSwitcher; - - public boolean hasDistinctMultitouch() { - return false; - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - - mSwitcher = new MockKeyboardSwitcher(); - - final String layoutSwitchBackSymbols = ""; - mSwitcher.loadKeyboard(layoutSwitchBackSymbols, hasDistinctMultitouch()); - } - - public void assertAlphabetNormal() { - assertTrue(mSwitcher.assertAlphabetNormal()); - } - - public void assertAlphabetManualShifted() { - assertTrue(mSwitcher.assertAlphabetManualShifted()); - } - - public void assertAlphabetAutomaticShifted() { - assertTrue(mSwitcher.assertAlphabetAutomaticShifted()); - } - - public void assertAlphabetShiftLocked() { - assertTrue(mSwitcher.assertAlphabetShiftLocked()); - } - - public void assertSymbolsNormal() { - assertTrue(mSwitcher.assertSymbolsNormal()); - } - - public void assertSymbolsShifted() { - assertTrue(mSwitcher.assertSymbolsShifted()); - } - - // Initial state test. - public void testLoadKeyboard() { - assertAlphabetNormal(); - } - - // Shift key in alphabet mode. - public void testShift() { - // Press/release shift key, enter into shift state. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - mSwitcher.onCodeInput(CODE_SHIFT); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertAlphabetManualShifted(); - // Press/release shift key, back to normal state. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - mSwitcher.onCodeInput(CODE_SHIFT); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertAlphabetNormal(); - - // Press/release shift key, enter into shift state. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - mSwitcher.onCodeInput(CODE_SHIFT); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertAlphabetManualShifted(); - // Press/release letter key, snap back to normal state. - mSwitcher.onPressKey('Z'); - mSwitcher.onCodeInput('Z'); - mSwitcher.onReleaseKey('Z'); - assertAlphabetNormal(); - } - - // Shift key sliding input. - public void testShiftSliding() { - // Press shift key. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - // Slide out shift key. - mSwitcher.onReleaseKey(CODE_SHIFT, SLIDING); - assertAlphabetManualShifted(); - - // Enter into letter key. - mSwitcher.onPressKey('Z'); - assertAlphabetManualShifted(); - // Release letter key, snap back to alphabet. - mSwitcher.onCodeInput('Z'); - mSwitcher.onReleaseKey('Z'); - assertAlphabetNormal(); - } - - public void enterSymbolsMode() { - // Press/release "?123" key. - mSwitcher.onPressKey(CODE_SYMBOL); - assertSymbolsNormal(); - mSwitcher.onCodeInput(CODE_SYMBOL); - mSwitcher.onReleaseKey(CODE_SYMBOL); - assertSymbolsNormal(); - } - - public void leaveSymbolsMode() { - // Press/release "ABC" key. - mSwitcher.onPressKey(CODE_SYMBOL); - assertAlphabetNormal(); - mSwitcher.onCodeInput(CODE_SYMBOL); - mSwitcher.onReleaseKey(CODE_SYMBOL); - assertAlphabetNormal(); - } - - // Switching between alphabet and symbols. - public void testAlphabetAndSymbols() { - enterSymbolsMode(); - leaveSymbolsMode(); - } - - // Switching between alphabet shift locked and symbols. - public void testAlphabetShiftLockedAndSymbols() { - enterShiftLockWithLongPressShift(); - enterSymbolsMode(); - - // Press/release "ABC" key, switch back to shift locked mode. - mSwitcher.onPressKey(CODE_SYMBOL); - assertAlphabetShiftLocked(); - mSwitcher.onCodeInput(CODE_SYMBOL); - mSwitcher.onReleaseKey(CODE_SYMBOL); - assertAlphabetShiftLocked(); - } - - // Symbols key sliding input. - public void testSymbolsSliding() { - // Press "123?" key. - mSwitcher.onPressKey(CODE_SYMBOL); - assertSymbolsNormal(); - // Slide out from "123?" key. - mSwitcher.onReleaseKey(CODE_SYMBOL, SLIDING); - assertSymbolsNormal(); - - // Enter into letter key. - mSwitcher.onPressKey('z'); - assertSymbolsNormal(); - // Release letter key, snap back to alphabet. - mSwitcher.onCodeInput('z'); - mSwitcher.onReleaseKey('z'); - assertAlphabetNormal(); - } - - // Switching between symbols and symbols shifted. - public void testSymbolsAndSymbolsShifted() { - enterSymbolsMode(); - - // Press/release "=\<" key. - mSwitcher.onPressKey(CODE_SHIFT); - assertSymbolsShifted(); - mSwitcher.onCodeInput(CODE_SHIFT); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertSymbolsShifted(); - - // Press/release "?123" key. - mSwitcher.onPressKey(CODE_SHIFT); - assertSymbolsNormal(); - mSwitcher.onCodeInput(CODE_SHIFT); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertSymbolsNormal(); - - leaveSymbolsMode(); - } - - // Symbols shift sliding input - public void testSymbolsShiftSliding() { - enterSymbolsMode(); - - // Press "=\<" key. - mSwitcher.onPressKey(CODE_SHIFT); - assertSymbolsShifted(); - // Slide out "=\<" key. - mSwitcher.onReleaseKey(CODE_SHIFT, SLIDING); - assertSymbolsShifted(); - - // Enter into symbol shifted letter key. - mSwitcher.onPressKey('~'); - assertSymbolsShifted(); - // Release symbol shifted letter key, snap back to symbols. - mSwitcher.onCodeInput('~'); - mSwitcher.onReleaseKey('~'); - assertSymbolsNormal(); - } - - // Symbols shift sliding input from symbols shifted. - public void testSymbolsShiftSliding2() { - enterSymbolsMode(); - - // Press/release "=\<" key. - mSwitcher.onPressKey(CODE_SHIFT); - assertSymbolsShifted(); - mSwitcher.onCodeInput(CODE_SHIFT); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertSymbolsShifted(); - - // Press "123?" key. - mSwitcher.onPressKey(CODE_SHIFT); - assertSymbolsNormal(); - // Slide out "123?" key. - mSwitcher.onReleaseKey(CODE_SHIFT, SLIDING); - assertSymbolsNormal(); - - // Enter into symbol letter key. - mSwitcher.onPressKey('1'); - assertSymbolsNormal(); - // Release symbol letter key, snap back to symbols shift. - mSwitcher.onCodeInput('1'); - mSwitcher.onReleaseKey('1'); - assertSymbolsShifted(); - } - - // Automatic snap back to alphabet from symbols by space key. - public void testSnapBackBySpace() { - enterSymbolsMode(); - - // Enter a symbol letter. - mSwitcher.onPressKey('1'); - assertSymbolsNormal(); - mSwitcher.onCodeInput('1'); - mSwitcher.onReleaseKey('1'); - assertSymbolsNormal(); - // Enter space, snap back to alphabet. - mSwitcher.onPressKey(CODE_SPACE); - assertSymbolsNormal(); - mSwitcher.onCodeInput(CODE_SPACE); - mSwitcher.onReleaseKey(CODE_SPACE); - assertAlphabetNormal(); - } - - // TODO: Add automatic snap back to shift locked test. - - // Automatic snap back to alphabet from symbols by registered letters. - public void testSnapBack() { - final String snapBackChars = "'"; - final int snapBackCode = snapBackChars.codePointAt(0); - final boolean hasDistinctMultitouch = true; - mSwitcher.loadKeyboard(snapBackChars, hasDistinctMultitouch); - - enterSymbolsMode(); - - // Enter a symbol letter. - mSwitcher.onPressKey('1'); - assertSymbolsNormal(); - mSwitcher.onCodeInput('1'); - mSwitcher.onReleaseKey('1'); - assertSymbolsNormal(); - // Enter snap back letter, snap back to alphabet. - mSwitcher.onPressKey(snapBackCode); - assertSymbolsNormal(); - mSwitcher.onCodeInput(snapBackCode); - mSwitcher.onReleaseKey(snapBackCode); - assertAlphabetNormal(); - } - - // Automatic upper case test - public void testAutomaticUpperCase() { - mSwitcher.setAutoCapsMode(AUTO_CAPS); - // Update shift state with auto caps enabled. - mSwitcher.updateShiftState(); - assertAlphabetAutomaticShifted(); - - // Press shift key. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - // Release shift key. - mSwitcher.onCodeInput(CODE_SHIFT); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertAlphabetNormal(); - } - - // Sliding from shift key in automatic upper case. - public void testAutomaticUpperCaseSliding() { - mSwitcher.setAutoCapsMode(AUTO_CAPS); - // Update shift state with auto caps enabled. - mSwitcher.updateShiftState(); - assertAlphabetAutomaticShifted(); - - // Press shift key. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - // Slide out shift key. - mSwitcher.onReleaseKey(CODE_SHIFT, SLIDING); - assertAlphabetManualShifted(); - // Enter into letter key. - mSwitcher.onPressKey('Z'); - assertAlphabetManualShifted(); - // Release letter key, snap back to alphabet. - mSwitcher.onCodeInput('Z'); - mSwitcher.onReleaseKey('Z'); - assertAlphabetNormal(); - } - - // Sliding from symbol key in automatic upper case. - public void testAutomaticUpperCaseSliding2() { - mSwitcher.setAutoCapsMode(AUTO_CAPS); - // Update shift state with auto caps enabled. - mSwitcher.updateShiftState(); - assertAlphabetAutomaticShifted(); - - // Press "123?" key. - mSwitcher.onPressKey(CODE_SYMBOL); - assertSymbolsNormal(); - // Slide out "123?" key. - mSwitcher.onReleaseKey(CODE_SYMBOL, SLIDING); - assertSymbolsNormal(); - // Enter into symbol letter keys. - mSwitcher.onPressKey('1'); - assertSymbolsNormal(); - // Release symbol letter key, snap back to alphabet. - mSwitcher.onCodeInput('1'); - mSwitcher.onReleaseKey('1'); - assertAlphabetNormal(); - } - - public void enterShiftLockWithLongPressShift() { - // Long press shift key - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - // Long press recognized in LatinKeyboardView.KeyTimerHandler. - mSwitcher.onCodeInput(CODE_CAPSLOCK); - assertAlphabetShiftLocked(); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertAlphabetShiftLocked(); - } - - public void leaveShiftLockWithLongPressShift() { - // Press shift key. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - // Long press recognized in LatinKeyboardView.KeyTimerHandler. - mSwitcher.onCodeInput(CODE_CAPSLOCK); - assertAlphabetNormal(); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertAlphabetNormal(); - } - - // Long press shift key. - // TODO: Move long press recognizing timer/logic into KeyboardState. - public void testLongPressShift() { - enterShiftLockWithLongPressShift(); - leaveShiftLockWithLongPressShift(); - } - - // Leave shift lock with single tap shift key. - public void testShiftInShiftLock() { - enterShiftLockWithLongPressShift(); - assertAlphabetShiftLocked(); - - // Tap shift key. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - mSwitcher.onCodeInput(CODE_SHIFT, SINGLE); - assertAlphabetManualShifted(); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertAlphabetNormal(); - } - - // Double tap shift key. - // TODO: Move double tap recognizing timer/logic into KeyboardState. - public void testDoubleTapShift() { - // First shift key tap. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - mSwitcher.onCodeInput(CODE_SHIFT); - assertAlphabetManualShifted(); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertAlphabetManualShifted(); - // Second shift key tap. - // Double tap recognized in LatinKeyboardView.KeyTimerHandler. - mSwitcher.onCodeInput(CODE_CAPSLOCK); - assertAlphabetShiftLocked(); - - // First shift key tap. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - mSwitcher.onCodeInput(CODE_SHIFT); - assertAlphabetManualShifted(); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertAlphabetNormal(); - // Second shift key tap. - // Second tap is ignored in LatinKeyboardView.KeyTimerHandler. - } - - // Update shift state. - public void testUpdateShiftState() { - mSwitcher.setAutoCapsMode(AUTO_CAPS); - // Update shift state. - mSwitcher.updateShiftState(); - assertAlphabetAutomaticShifted(); - } - - // Update shift state when shift locked. - public void testUpdateShiftStateInShiftLocked() { - mSwitcher.setAutoCapsMode(AUTO_CAPS); - enterShiftLockWithLongPressShift(); - assertAlphabetShiftLocked(); - // Update shift state when shift locked - mSwitcher.updateShiftState(); - assertAlphabetShiftLocked(); - } - - // TODO: Change focus test. - - // TODO: Change orientation test. -} diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java new file mode 100644 index 000000000..d13ca632a --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.keyboard.internal; + +public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase { + // Shift key in alphabet mode. + public void testShift() { + // Press/release shift key, enter into shift state. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + // Press/release shift key, back to normal state. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); + + // Press/release shift key, enter into shift state. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + // Press/release letter key, switch back to normal state. + pressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); + } + + // Shift key sliding input. + public void testShiftSliding() { + // Press and slide from shift key. + pressAndSlideFromKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + + // Enter/release letter key, switch back to alphabet. + pressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); + } + + // Switching between alphabet and symbols. + public void testAlphabetAndSymbols() { + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release "?123" key, back to alphabet. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + } + + // Switching between alphabet shift locked and symbols. + public void testAlphabetShiftLockedAndSymbols() { + // Long press shift key, enter alphabet shift locked. + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); + + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Press/release "ABC" key, switch back to shift locked mode. + pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + } + + // Symbols key sliding input. + public void testSymbolsSliding() { + // Press and slide from "123?" key. + pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Enter/release into symbol key, switch back to alphabet. + pressAndReleaseKey('!', SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED); + } + + // Switching between symbols and symbols shifted. + public void testSymbolsAndSymbolsShifted() { + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + } + + // Symbols shift sliding input + public void testSymbolsShiftSliding() { + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Press and slide from "=\<" key. + pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + + // Enter/release symbol shifted letter key, switch back to symbols. + pressAndReleaseKey('~', SYMBOLS_SHIFTED, SYMBOLS_UNSHIFTED); + } + + // Symbols shift sliding input from symbols shifted. + public void testSymbolsShiftSliding2() { + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Press/release "=\<" key, enter into symbols shifted. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + + // Press and slide from "123?" key. + pressAndSlideFromKey(CODE_SHIFT, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Enter/release symbol letter key, switch back to symbols shifted. + pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_SHIFTED); + } + + // Automatic switch back to alphabet from symbols by space key. + public void testSwitchBackBySpace() { + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Enter a symbol letter. + pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Enter space, switch back to alphabet. + pressAndReleaseKey(CODE_SPACE, SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED); + } + + // Automatic switch back to shift locked test. + public void testSwitchBackBySpaceInShiftLocked() { + // Long press shift key, enter alphabet shift locked. + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); + + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Enter a symbol letter. + pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Enter space, switch back to alphabet. + pressAndReleaseKey(CODE_SPACE, SYMBOLS_UNSHIFTED, ALPHABET_SHIFT_LOCKED); + } + + + // Automatic switch back to alphabet from symbols by registered letters. + public void testSwitchBackChar() { + // Set switch back chars. + final String switchBackSymbols = "'"; + final int switchBackCode = switchBackSymbols.codePointAt(0); + setLayoutSwitchBackSymbols(switchBackSymbols); + loadKeyboard(ALPHABET_UNSHIFTED); + + // Press/release "?123" key, enter into symbols. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Enter a symbol letter. + pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Enter switch back letter, switch back to alphabet. + pressAndReleaseKey(switchBackCode, SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED); + } + + // Automatic upper case test + public void testAutomaticUpperCase() { + // Set auto caps mode on. + setAutoCapsMode(AUTO_CAPS); + + // Update shift state with auto caps enabled. + updateShiftState(ALPHABET_AUTOMATIC_SHIFTED); + + // Press/release shift key, back to alphabet. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); + } + + // Sliding from shift key in automatic upper case. + public void testAutomaticUpperCaseSliding() { + // Set auto caps mode on. + setAutoCapsMode(AUTO_CAPS); + + // Update shift state with auto caps enabled. + updateShiftState(ALPHABET_AUTOMATIC_SHIFTED); + + // Press and slide from shift key. + pressAndSlideFromKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + + // Enter and release letter key, back to alphabet. + pressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); + } + + // Sliding from symbol key in automatic upper case. + public void testAutomaticUpperCaseSliding2() { + // Set auto caps mode on. + setAutoCapsMode(AUTO_CAPS); + + // Update shift state with auto caps enabled. + updateShiftState(ALPHABET_AUTOMATIC_SHIFTED); + + // Press and slide from "123?" key. + pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + + // Enter and release symbol letter keys, back to alphabet. + pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, ALPHABET_UNSHIFTED); + } + + // Long press shift key. + // TODO: Move long press recognizing timer/logic into KeyboardState. + public void testLongPressShift() { + // Long press shift key, enter alphabet shift locked. + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); + + // Press/release letter key, remain in shift locked. + pressAndReleaseKey('A', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Press/release letter key, remain in shift locked. + pressAndReleaseKey('B', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Press/release word separator, remain in shift locked. + pressAndReleaseKey(CODE_SPACE, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED); + + // Long press shift key, back to alphabet. + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); + } + + // Leave shift lock with single tap shift key. + public void testShiftInShiftLock() { + // Long press shift key, enter alphabet shift locked. + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); + + // Press/release shift key, back to alphabet. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); + } + + // Double tap shift key. + // TODO: Move double tap recognizing timer/logic into KeyboardState. + public void testDoubleTapShift() { + // First shift key tap. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + + // Second shift key tap. + // Double tap recognized in LatinKeyboardView.KeyTimerHandler. + secondTapShiftKey(ALPHABET_SHIFT_LOCKED); + + // First shift key tap. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); + + // Second shift key tap. + // Second tap is ignored in LatinKeyboardView.KeyTimerHandler. + } + + // Update shift state. + public void testUpdateShiftState() { + // Set auto caps mode on. + setAutoCapsMode(AUTO_CAPS); + + // Update shift state. + updateShiftState(ALPHABET_AUTOMATIC_SHIFTED); + + // Press/release letter key, back to alphabet. + pressAndReleaseKey('A', ALPHABET_AUTOMATIC_SHIFTED, ALPHABET_UNSHIFTED); + + // Press/release letter key + pressAndReleaseKey('b', ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED); + + // Press/release auto caps trigger letter, back to automatic shifted. + pressAndReleaseKey(CODE_AUTO_CAPS_TRIGGER, ALPHABET_UNSHIFTED, ALPHABET_AUTOMATIC_SHIFTED); + } + + // Update shift state when shift locked. + public void testUpdateShiftStateInShiftLocked() { + // Set auto caps mode on. + setAutoCapsMode(AUTO_CAPS); + + // Long press shift key, enter alphabet shift locked. + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); + + // Update shift state when shift locked + updateShiftState(ALPHABET_SHIFT_LOCKED); + } + + // Change focus to new text field. + public void testChangeFocus() { + // Press/release shift key. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + // Change focus to new text field. + loadKeyboard(ALPHABET_UNSHIFTED); + + // Long press shift key, enter alphabet shift locked. + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); + // Change focus to new text field. + loadKeyboard(ALPHABET_UNSHIFTED); + + // Press/release symbol key. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Change focus to new text field. + loadKeyboard(ALPHABET_UNSHIFTED); + + // Press/release symbol key. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release shift key. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Change focus to new text field. + loadKeyboard(ALPHABET_UNSHIFTED); + } + + // Change focus to auto caps text field. + public void testChangeFocusAutoCaps() { + // Set auto caps mode on. + setAutoCapsMode(AUTO_CAPS); + + // Update shift state. + updateShiftState(ALPHABET_AUTOMATIC_SHIFTED); + // Change focus to new text field. + loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); + + // Press/release shift key, enter alphabet. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED); + // Change focus to new text field. + loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); + + // Long press shift key, enter alphabet shift locked. + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); + // Change focus to new text field. + loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); + + // Press/release symbol key. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Change focus to new text field. + loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); + + // Press/release symbol key. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Press/release shift key. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Change focus to new text field. + loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED); + } + + // Change orientation. + public void testChangeOrientation() { + // Press/release shift key. + pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED); + // Rotate device. + rotateDevice(ALPHABET_MANUAL_SHIFTED); + + // Long press shift key, enter alphabet shift locked. + longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED); + // Rotate device. + rotateDevice(ALPHABET_SHIFT_LOCKED); + + // Press/release symbol key. + pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED); + // Rotate device. + rotateDevice(SYMBOLS_UNSHIFTED); + + // Press/release shift key. + pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED); + // Rotate device. + rotateDevice(SYMBOLS_SHIFTED); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTests.java deleted file mode 100644 index 19339f72e..000000000 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTests.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.keyboard.internal; - -public class KeyboardStateTests extends KeyboardStateNonDistinctTests { - @Override - public boolean hasDistinctMultitouch() { - return true; - } - - // Shift key chording input. - public void testShiftChording() { - // Press shift key and hold, enter into choring shift state. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - - // Press/release letter keys. - mSwitcher.onPressKey('Z'); - mSwitcher.onCodeInput('Z', MULTI); - mSwitcher.onReleaseKey('Z'); - assertAlphabetManualShifted(); - mSwitcher.onPressKey('X'); - mSwitcher.onCodeInput('X', MULTI); - mSwitcher.onReleaseKey('X'); - assertAlphabetManualShifted(); - - // Release shift key, snap back to normal state. - mSwitcher.onCodeInput(CODE_SHIFT); - mSwitcher.onReleaseKey(CODE_SHIFT); - mSwitcher.updateShiftState(); - assertAlphabetNormal(); - } - - // Symbols key chording input. - public void testSymbolsChording() { - // Press symbols key and hold, enter into choring shift state. - mSwitcher.onPressKey(CODE_SYMBOL); - assertSymbolsNormal(); - - // Press/release symbol letter keys. - mSwitcher.onPressKey('1'); - mSwitcher.onCodeInput('1', MULTI); - mSwitcher.onReleaseKey('1'); - assertSymbolsNormal(); - mSwitcher.onPressKey('2'); - mSwitcher.onCodeInput('2', MULTI); - mSwitcher.onReleaseKey('2'); - assertSymbolsNormal(); - - // Release shift key, snap back to normal state. - mSwitcher.onCodeInput(CODE_SYMBOL); - mSwitcher.onReleaseKey(CODE_SYMBOL); - mSwitcher.updateShiftState(); - assertAlphabetNormal(); - } - - // Chording shift key in automatic upper case. - public void testAutomaticUpperCaseChording() { - mSwitcher.setAutoCapsMode(AUTO_CAPS); - // Update shift state with auto caps enabled. - mSwitcher.updateShiftState(); - assertAlphabetAutomaticShifted(); - - // Press shift key. - mSwitcher.onPressKey(CODE_SHIFT); - assertAlphabetManualShifted(); - // Press/release letter keys. - mSwitcher.onPressKey('Z'); - mSwitcher.onCodeInput('Z', MULTI); - mSwitcher.onReleaseKey('Z'); - assertAlphabetManualShifted(); - // Release shift key, snap back to alphabet. - mSwitcher.onCodeInput(CODE_SHIFT); - mSwitcher.onReleaseKey(CODE_SHIFT); - assertAlphabetNormal(); - } - - // Chording symbol key in automatic upper case. - public void testAutomaticUpperCaseChrding2() { - mSwitcher.setAutoCapsMode(AUTO_CAPS); - // Update shift state with auto caps enabled. - mSwitcher.updateShiftState(); - assertAlphabetAutomaticShifted(); - - // Press "123?" key. - mSwitcher.onPressKey(CODE_SYMBOL); - assertSymbolsNormal(); - // Press/release symbol letter keys. - mSwitcher.onPressKey('1'); - assertSymbolsNormal(); - mSwitcher.onCodeInput('1', MULTI); - mSwitcher.onReleaseKey('1'); - assertSymbolsNormal(); - // Release "123?" key, snap back to alphabet. - mSwitcher.onCodeInput(CODE_SYMBOL); - mSwitcher.onReleaseKey(CODE_SYMBOL); - assertAlphabetNormal(); - } - - // TODO: Multitouch test - - // TODO: n-Keys roll over test -} diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java new file mode 100644 index 000000000..62df2cfe2 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateTestsBase.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.keyboard.internal; + +import android.test.AndroidTestCase; + +public class KeyboardStateTestsBase extends AndroidTestCase + implements MockKeyboardSwitcher.Constants { + protected MockKeyboardSwitcher mSwitcher; + + private String mLayoutSwitchBackSymbols = ""; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mSwitcher = new MockKeyboardSwitcher(); + mSwitcher.setAutoCapsMode(NO_AUTO_CAPS); + + loadKeyboard(ALPHABET_UNSHIFTED); + } + + public void setAutoCapsMode(boolean autoCaps) { + mSwitcher.setAutoCapsMode(autoCaps); + } + + public void setLayoutSwitchBackSymbols(String switchBackSymbols) { + mLayoutSwitchBackSymbols = switchBackSymbols; + } + + public void updateShiftState(int afterUpdate) { + mSwitcher.updateShiftState(); + assertEquals(afterUpdate, mSwitcher.getLayoutId()); + } + + public void loadKeyboard(int afterLoad) { + mSwitcher.loadKeyboard(mLayoutSwitchBackSymbols); + updateShiftState(afterLoad); + } + + public void rotateDevice(int afterRotate) { + mSwitcher.saveKeyboardState(); + mSwitcher.loadKeyboard(mLayoutSwitchBackSymbols); + assertEquals(afterRotate, mSwitcher.getLayoutId()); + } + + public void pressKey(int code, int afterPress) { + mSwitcher.onPressKey(code); + assertEquals(afterPress, mSwitcher.getLayoutId()); + } + + public void releaseKey(int code, int afterRelease) { + mSwitcher.onCodeInput(code, SINGLE); + mSwitcher.onReleaseKey(code, NOT_SLIDING); + assertEquals(afterRelease, mSwitcher.getLayoutId()); + } + + public void pressAndReleaseKey(int code, int afterPress, int afterRelease) { + pressKey(code, afterPress); + releaseKey(code, afterRelease); + } + + public void chordingPressKey(int code, int afterPress) { + pressKey(code, afterPress); + } + + public void chordingReleaseKey(int code, int afterRelease) { + mSwitcher.onCodeInput(code, MULTI); + mSwitcher.onReleaseKey(code, NOT_SLIDING); + assertEquals(afterRelease, mSwitcher.getLayoutId()); + } + + public void chordingPressAndReleaseKey(int code, int afterPress, int afterRelease) { + chordingPressKey(code, afterPress); + chordingReleaseKey(code, afterRelease); + } + + public void pressAndSlideFromKey(int code, int afterPress, int afterSlide) { + pressKey(code, afterPress); + mSwitcher.onReleaseKey(code, SLIDING); + assertEquals(afterSlide, mSwitcher.getLayoutId()); + } + + public void longPressShiftKey(int afterPress, int afterLongPress) { + // Long press shift key + mSwitcher.onPressKey(CODE_SHIFT); + assertEquals(afterPress, mSwitcher.getLayoutId()); + // Long press recognized in LatinKeyboardView.KeyTimerHandler. + mSwitcher.onCodeInput(CODE_CAPSLOCK, SINGLE); + assertEquals(afterLongPress, mSwitcher.getLayoutId()); + mSwitcher.onReleaseKey(CODE_SHIFT, NOT_SLIDING); + assertEquals(afterLongPress, mSwitcher.getLayoutId()); + } + + public void secondTapShiftKey(int afterTap) { + mSwitcher.onCodeInput(CODE_CAPSLOCK, SINGLE); + assertEquals(afterTap, mSwitcher.getLayoutId()); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java index 1e294d179..87b463609 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java @@ -21,10 +21,10 @@ import com.android.inputmethod.keyboard.internal.KeyboardState.SwitchActions; public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { public interface Constants { - // Argument for KeyboardState.onPressKey and onReleaseKey. + // Argument for {@link KeyboardState#onPressKey} and {@link KeyboardState#onReleaseKey}. public static final boolean NOT_SLIDING = false; public static final boolean SLIDING = true; - // Argument for KeyboardState.onCodeInput. + // Argument for {@link KeyboardState#onCodeInput}. public static final boolean SINGLE = true; public static final boolean MULTI = false; public static final boolean NO_AUTO_CAPS = false; @@ -34,18 +34,17 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { public static final int CODE_SYMBOL = Keyboard.CODE_SWITCH_ALPHA_SYMBOL; public static final int CODE_CAPSLOCK = Keyboard.CODE_CAPSLOCK; public static final int CODE_SPACE = Keyboard.CODE_SPACE; - } - - public static final String WORD_SEPARATORS = " ,."; + public static final int CODE_AUTO_CAPS_TRIGGER = Keyboard.CODE_SPACE; - private static final int ALPHABET_UNSHIFTED = 0; - private static final int ALPHABET_MANUAL_SHIFTED = 1; - private static final int ALPHABET_AUTOMATIC_SHIFTED = 2; - private static final int ALPHABET_SHIFT_LOCKED = 3; - private static final int SYMBOLS_UNSHIFTED = 4; - private static final int SYMBOLS_SHIFTED = 5; + public static final int ALPHABET_UNSHIFTED = 0; + public static final int ALPHABET_MANUAL_SHIFTED = 1; + public static final int ALPHABET_AUTOMATIC_SHIFTED = 2; + public static final int ALPHABET_SHIFT_LOCKED = 3; + public static final int SYMBOLS_UNSHIFTED = 4; + public static final int SYMBOLS_SHIFTED = 5; + } - private int mLayout = ALPHABET_UNSHIFTED; + private int mLayout = Constants.ALPHABET_UNSHIFTED; private boolean mAutoCapsMode = Constants.NO_AUTO_CAPS; // Following InputConnection's behavior. Simulating InputType.TYPE_TEXT_FLAG_CAP_WORDS. @@ -53,28 +52,8 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { private final KeyboardState mState = new KeyboardState(this); - public boolean assertAlphabetNormal() { - return mLayout == ALPHABET_UNSHIFTED; - } - - public boolean assertAlphabetManualShifted() { - return mLayout == ALPHABET_MANUAL_SHIFTED; - } - - public boolean assertAlphabetAutomaticShifted() { - return mLayout == ALPHABET_AUTOMATIC_SHIFTED; - } - - public boolean assertAlphabetShiftLocked() { - return mLayout == ALPHABET_SHIFT_LOCKED; - } - - public boolean assertSymbolsNormal() { - return mLayout == SYMBOLS_UNSHIFTED; - } - - public boolean assertSymbolsShifted() { - return mLayout == SYMBOLS_SHIFTED; + public int getLayoutId() { + return mLayout; } public void setAutoCapsMode(boolean autoCaps) { @@ -83,37 +62,37 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { @Override public void setAlphabetKeyboard() { - mLayout = ALPHABET_UNSHIFTED; + mLayout = Constants.ALPHABET_UNSHIFTED; } @Override public void setShifted(int shiftMode) { if (shiftMode == SwitchActions.UNSHIFT) { - mLayout = ALPHABET_UNSHIFTED; + mLayout = Constants.ALPHABET_UNSHIFTED; } else if (shiftMode == SwitchActions.MANUAL_SHIFT) { - mLayout = ALPHABET_MANUAL_SHIFTED; + mLayout = Constants.ALPHABET_MANUAL_SHIFTED; } else if (shiftMode == SwitchActions.AUTOMATIC_SHIFT) { - mLayout = ALPHABET_AUTOMATIC_SHIFTED; + mLayout = Constants.ALPHABET_AUTOMATIC_SHIFTED; } } @Override public void setShiftLocked(boolean shiftLocked) { if (shiftLocked) { - mLayout = ALPHABET_SHIFT_LOCKED; + mLayout = Constants.ALPHABET_SHIFT_LOCKED; } else { - mLayout = ALPHABET_UNSHIFTED; + mLayout = Constants.ALPHABET_UNSHIFTED; } } @Override public void setSymbolsKeyboard() { - mLayout = SYMBOLS_UNSHIFTED; + mLayout = Constants.SYMBOLS_UNSHIFTED; } @Override public void setSymbolsShiftedKeyboard() { - mLayout = SYMBOLS_SHIFTED; + mLayout = Constants.SYMBOLS_SHIFTED; } @Override @@ -125,29 +104,26 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { mState.onUpdateShiftState(mAutoCapsMode && mAutoCapsState); } - public void loadKeyboard(String layoutSwitchBackSymbols, - boolean hasDistinctMultitouch) { - mState.onLoadKeyboard(layoutSwitchBackSymbols, hasDistinctMultitouch); + public void loadKeyboard(String layoutSwitchBackSymbols) { + mState.onLoadKeyboard(layoutSwitchBackSymbols); } - public void onPressKey(int code) { - mState.onPressKey(code); + public void saveKeyboardState() { + mState.onSaveKeyboardState(); } - public void onReleaseKey(int code) { - onReleaseKey(code, Constants.NOT_SLIDING); + public void onPressKey(int code) { + mState.onPressKey(code); } public void onReleaseKey(int code, boolean withSliding) { mState.onReleaseKey(code, withSliding); } - public void onCodeInput(int code) { - onCodeInput(code, Constants.SINGLE); - } - public void onCodeInput(int code, boolean isSinglePointer) { - mAutoCapsState = (WORD_SEPARATORS.indexOf(code) >= 0); + if (Keyboard.isLetterCode(code)) { + mAutoCapsState = (code == Constants.CODE_AUTO_CAPS_TRIGGER); + } mState.onCodeInput(code, isSinglePointer, mAutoCapsMode && mAutoCapsState); } diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParserTests.java index 0eb324234..74aaf9af8 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParserTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParserTests.java @@ -25,11 +25,11 @@ import com.android.inputmethod.latin.R; public class MoreKeySpecParserTests extends AndroidTestCase { private Resources mRes; - private static final int ICON_SETTINGS_KEY = 5; + private static final int ICON_SETTINGS_KEY = R.styleable.Keyboard_iconSettingsKey; private static final int ICON_UNDEFINED = KeyboardIconsSet.ICON_UNDEFINED; private static final String CODE_SETTINGS = "@integer/key_settings"; - private static final String ICON_SETTINGS = "@icon/" + ICON_SETTINGS_KEY; + private static final String ICON_SETTINGS = "@icon/settingsKey"; private static final String CODE_NON_EXISTING = "@integer/non_existing"; private static final String ICON_NON_EXISTING = "@icon/non_existing"; @@ -53,7 +53,7 @@ public class MoreKeySpecParserTests extends AndroidTestCase { String actualOutputText = MoreKeySpecParser.getOutputText(moreKeySpec); assertEquals(message + ": ouptputText:", expectedOutputText, actualOutputText); - int actualIcon = MoreKeySpecParser.getIconId(moreKeySpec); + int actualIcon = MoreKeySpecParser.getIconAttrId(moreKeySpec); assertEquals(message + ": icon:", expectedIcon, actualIcon); int actualCode = MoreKeySpecParser.getCode(mRes, moreKeySpec); @@ -66,7 +66,7 @@ public class MoreKeySpecParserTests extends AndroidTestCase { assertParser(message, moreKeySpec, expectedLabel, expectedOutputText, expectedIcon, expectedCode); fail(message); - } catch (MoreKeySpecParser.MoreKeySpecParserError pcpe) { + } catch (Exception pcpe) { // success. } } @@ -89,78 +89,78 @@ public class MoreKeySpecParserTests extends AndroidTestCase { assertParser("Single escaped at", "\\@", "@", null, ICON_UNDEFINED, '@'); assertParser("Single letter with outputText", "a|abc", - "a", "abc", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a", "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Single letter with escaped outputText", "a|a\\|c", - "a", "a|c", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a", "a|c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Single letter with comma outputText", "a|a,b", - "a", "a,b", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Single letter with escaped comma outputText", "a|a\\,b", - "a", "a,b", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Single letter with outputText starts with at", "a|@bc", - "a", "@bc", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a", "@bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Single letter with outputText contains at", "a|a@c", - "a", "a@c", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a", "a@c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Single letter with escaped at outputText", "a|\\@bc", - "a", "@bc", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a", "@bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Single escaped escape with outputText", "\\\\|\\\\", - "\\", "\\", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "\\", "\\", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Single escaped bar with outputText", "\\||\\|", - "|", "|", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "|", "|", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Single letter with code", "a|" + CODE_SETTINGS, "a", null, ICON_UNDEFINED, mCodeSettings); } public void testLabel() { assertParser("Simple label", "abc", - "abc", "abc", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with escaped bar", "a\\|c", - "a|c", "a|c", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a|c", "a|c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with escaped escape", "a\\\\c", - "a\\c", "a\\c", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a\\c", "a\\c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with comma", "a,c", - "a,c", "a,c", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a,c", "a,c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with escaped comma", "a\\,c", - "a,c", "a,c", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a,c", "a,c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label starts with at", "@bc", - "@bc", "@bc", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "@bc", "@bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label contains at", "a@c", - "a@c", "a@c", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a@c", "a@c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with escaped at", "\\@bc", - "@bc", "@bc", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "@bc", "@bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with escaped letter", "\\abc", - "abc", "abc", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with outputText", "abc|def", - "abc", "def", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with comma and outputText", "a,c|def", - "a,c", "def", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a,c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Escaped comma label with outputText", "a\\,c|def", - "a,c", "def", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a,c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Escaped label with outputText", "a\\|c|def", - "a|c", "def", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a|c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with escaped bar outputText", "abc|d\\|f", - "abc", "d|f", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", "d|f", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Escaped escape label with outputText", "a\\\\|def", - "a\\", "def", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a\\", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label starts with at and outputText", "@bc|def", - "@bc", "def", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "@bc", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label contains at label and outputText", "a@c|def", - "a@c", "def", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a@c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Escaped at label with outputText", "\\@bc|def", - "@bc", "def", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "@bc", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with comma outputText", "abc|a,b", - "abc", "a,b", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with escaped comma outputText", "abc|a\\,b", - "abc", "a,b", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with outputText starts with at", "abc|@bc", - "abc", "@bc", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", "@bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with outputText contains at", "abc|a@c", - "abc", "a@c", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", "a@c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with escaped at outputText", "abc|\\@bc", - "abc", "@bc", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", "@bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with escaped bar outputText", "abc|d\\|f", - "abc", "d|f", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", "d|f", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Escaped bar label with escaped bar outputText", "a\\|c|d\\|f", - "a|c", "d|f", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a|c", "d|f", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label with code", "abc|" + CODE_SETTINGS, "abc", null, ICON_UNDEFINED, mCodeSettings); assertParser("Escaped label with code", "a\\|c|" + CODE_SETTINGS, @@ -169,13 +169,13 @@ public class MoreKeySpecParserTests extends AndroidTestCase { public void testIconAndCode() { assertParser("Icon with outputText", ICON_SETTINGS + "|abc", - null, "abc", ICON_SETTINGS_KEY, Keyboard.CODE_UNSPECIFIED); + null, "abc", ICON_SETTINGS_KEY, Keyboard.CODE_OUTPUT_TEXT); assertParser("Icon with outputText starts with at", ICON_SETTINGS + "|@bc", - null, "@bc", ICON_SETTINGS_KEY, Keyboard.CODE_UNSPECIFIED); + null, "@bc", ICON_SETTINGS_KEY, Keyboard.CODE_OUTPUT_TEXT); assertParser("Icon with outputText contains at", ICON_SETTINGS + "|a@c", - null, "a@c", ICON_SETTINGS_KEY, Keyboard.CODE_UNSPECIFIED); + null, "a@c", ICON_SETTINGS_KEY, Keyboard.CODE_OUTPUT_TEXT); assertParser("Icon with escaped at outputText", ICON_SETTINGS + "|\\@bc", - null, "@bc", ICON_SETTINGS_KEY, Keyboard.CODE_UNSPECIFIED); + null, "@bc", ICON_SETTINGS_KEY, Keyboard.CODE_OUTPUT_TEXT); assertParser("Label starts with at and code", "@bc|" + CODE_SETTINGS, "@bc", null, ICON_UNDEFINED, mCodeSettings); assertParser("Label contains at and code", "a@c|" + CODE_SETTINGS, @@ -201,8 +201,8 @@ public class MoreKeySpecParserTests extends AndroidTestCase { null, null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); assertParserError("Icon without code", ICON_SETTINGS, null, null, ICON_SETTINGS_KEY, Keyboard.CODE_UNSPECIFIED); - assertParser("Non existing icon", ICON_NON_EXISTING + "|abc", - null, "abc", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + assertParserError("Non existing icon", ICON_NON_EXISTING + "|abc", + null, "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); assertParserError("Non existing code", "abc|" + CODE_NON_EXISTING, "abc", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); assertParserError("Third bar at end", "a|b|", diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java new file mode 100644 index 000000000..06ee5bffa --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.test.ServiceTestCase; +import android.text.InputType; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.view.View; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardActionListener; + +public class InputLogicTests extends ServiceTestCase<LatinIME> { + + private static final String PREF_DEBUG_MODE = "debug_mode"; + + private LatinIME mLatinIME; + private TextView mTextView; + + public InputLogicTests() { + super(LatinIME.class); + } + + // returns the previous setting value + private boolean setDebugMode(final boolean mode) { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mLatinIME); + final boolean previousDebugSetting = prefs.getBoolean(PREF_DEBUG_MODE, false); + final SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(PREF_DEBUG_MODE, true); + editor.commit(); + return previousDebugSetting; + } + + @Override + protected void setUp() { + try { + super.setUp(); + } catch (Exception e) { + e.printStackTrace(); + } + mTextView = new TextView(getContext()); + mTextView.setInputType(InputType.TYPE_CLASS_TEXT); + mTextView.setEnabled(true); + setupService(); + mLatinIME = getService(); + final boolean previousDebugSetting = setDebugMode(true); + mLatinIME.onCreate(); + setDebugMode(previousDebugSetting); + final EditorInfo ei = new EditorInfo(); + final InputConnection ic = mTextView.onCreateInputConnection(ei); + final LayoutInflater inflater = + (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + final ViewGroup vg = new FrameLayout(getContext()); + final View inputView = inflater.inflate(R.layout.input_view, vg); + mLatinIME.setInputView(inputView); + mLatinIME.onBindInput(); + mLatinIME.onCreateInputView(); + mLatinIME.onStartInputView(ei, false); + mLatinIME.onCreateInputMethodInterface().startInput(ic, ei); + } + + // type(int) and type(String): helper methods to send a code point resp. a string to LatinIME. + private void type(final int codePoint) { + // onPressKey and onReleaseKey are explicitly deactivated here, but they do happen in the + // code (although multitouch/slide input and other factors make the sequencing complicated). + // They are supposed to be entirely deconnected from the input logic from LatinIME point of + // view and only delegates to the parts of the code that care. So we don't include them here + // to keep these tests as pinpoint as possible and avoid bringing it too many dependencies, + // but keep them in mind if something breaks. Commenting them out as is should work. + //mLatinIME.onPressKey(codePoint); + mLatinIME.onCodeInput(codePoint, new int[] { codePoint }, + KeyboardActionListener.NOT_A_TOUCH_COORDINATE, + KeyboardActionListener.NOT_A_TOUCH_COORDINATE); + //mLatinIME.onReleaseKey(codePoint, false); + } + + private void type(final String stringToType) { + for (int i = 0; i < stringToType.length(); ++i) { + type(stringToType.codePointAt(i)); + } + } + + public void testTypeWord() { + final String wordToType = "abcd"; + type(wordToType); + assertEquals("type word", wordToType, mTextView.getText().toString()); + } + + public void testPickSuggestionThenBackspace() { + final String wordToType = "tgis"; + type(wordToType); + mLatinIME.pickSuggestionManually(0, wordToType); + type(Keyboard.CODE_DELETE); + assertEquals("press suggestion then backspace", wordToType, mTextView.getText().toString()); + } + +} |