Loading java/res/values/attrs.xml +3 −0 Original line number Diff line number Diff line Loading @@ -242,6 +242,9 @@ <flag name="withIconLeft" value="0x1000" /> <flag name="withIconRight" value="0x2000" /> <flag name="autoXScale" value="0x4000" /> <!-- If true, character case of code, altCode, moreKeys, keyOutputText, keyLabel, or keyHintLabel will never be subject to change. --> <flag name="preserveCase" value="0x8000" /> </attr> <!-- The icon to display on the key instead of the label. --> <attr name="keyIcon" format="enum"> Loading java/res/xml/keyboard_set.xml +2 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,8 @@ latin:keyboardLocale="en_GB,en_US"> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_qwerty" /> latin:elementKeyboard="@xml/kbd_qwerty" latin:elementAutoGenerate="true" /> <Element latin:elementName="alphabetManualShifted" latin:elementKeyboard="@xml/kbd_qwerty" Loading java/src/com/android/inputmethod/keyboard/Key.java +56 −20 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ public class Key { private static final int LABEL_FLAGS_WITH_ICON_LEFT = 0x1000; private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000; private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000; private static final int LABEL_FLAGS_PRESERVE_CASE = 0x8000; /** Icon to display instead of a label. Icon takes precedence over a label */ private final int mIconAttrId; Loading Loading @@ -262,19 +263,6 @@ public class Key { // Update row to have current x coordinate. row.setXPos(keyXPos + keyWidth); final String[] moreKeys = style.getStringArray(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. if (params.mId.isAlphabetKeyboard() && !res.getBoolean(R.bool.config_digit_more_keys_enabled)) { mMoreKeys = MoreKeySpecParser.filterOut(res, moreKeys, MoreKeySpecParser.DIGIT_FILTER); } else { mMoreKeys = moreKeys; } mMaxMoreKeysColumn = style.getInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMiniKeyboardColumn); mBackgroundType = style.getInt(keyAttr, R.styleable.Keyboard_Key_backgroundType, BACKGROUND_TYPE_NORMAL); mActionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags, 0); Loading @@ -292,14 +280,39 @@ public class Key { mDisabledIconAttrId = KeyboardIconsSet.getIconAttrId(style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconDisabled, KeyboardIconsSet.ICON_UNDEFINED)); mLabel = style.getString(keyAttr, R.styleable.Keyboard_Key_keyLabel); mHintLabel = style.getString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags, 0); mOutputText = style.getString(keyAttr, R.styleable.Keyboard_Key_keyOutputText); final boolean preserveCase = (mLabelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0; final String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); if (moreKeys != null) { for (int i = 0; i < moreKeys.length; i++) { moreKeys[i] = adjustCaseOfStringForKeyboardId( moreKeys[i], preserveCase, params.mId); } } // TODO: Add new key label flag to control this. // In Arabic symbol layouts, we'd like to keep digits in more keys regardless of // config_digit_more_keys_enabled. if (params.mId.isAlphabetKeyboard() && !res.getBoolean(R.bool.config_digit_more_keys_enabled)) { mMoreKeys = MoreKeySpecParser.filterOut(res, moreKeys, MoreKeySpecParser.DIGIT_FILTER); } else { mMoreKeys = moreKeys; } mMaxMoreKeysColumn = style.getInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMiniKeyboardColumn); mLabel = adjustCaseOfStringForKeyboardId(style.getString( keyAttr, R.styleable.Keyboard_Key_keyLabel), preserveCase, params.mId); mHintLabel = adjustCaseOfStringForKeyboardId(style.getString( keyAttr, R.styleable.Keyboard_Key_keyHintLabel), preserveCase, params.mId); mOutputText = adjustCaseOfStringForKeyboardId(style.getString( keyAttr, R.styleable.Keyboard_Key_keyOutputText), preserveCase, params.mId); // Choose the first letter of the label as primary code if not // specified. final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code, Keyboard.CODE_UNSPECIFIED); final int code = adjustCaseOfCodeForKeyboardId(style.getInt( keyAttr, R.styleable.Keyboard_Key_code, Keyboard.CODE_UNSPECIFIED), preserveCase, params.mId); if (code == Keyboard.CODE_UNSPECIFIED && mOutputText == null && !TextUtils.isEmpty(mLabel)) { if (mLabel.length() != 1) { Loading @@ -312,13 +325,36 @@ public class Key { } else { mCode = code; } mAltCode = style.getInt(keyAttr, R.styleable.Keyboard_Key_altCode, Keyboard.CODE_UNSPECIFIED); mAltCode = adjustCaseOfCodeForKeyboardId(style.getInt(keyAttr, R.styleable.Keyboard_Key_altCode, Keyboard.CODE_UNSPECIFIED), preserveCase, params.mId); mHashCode = hashCode(this); keyAttr.recycle(); } private static int adjustCaseOfCodeForKeyboardId(int code, boolean preserveCase, KeyboardId id) { if (!Keyboard.isLetterCode(code) || preserveCase) return code; final String text = new String(new int[] { code } , 0, 1); final String casedText = adjustCaseOfStringForKeyboardId(text, preserveCase, id); return casedText.codePointAt(0); } private static String adjustCaseOfStringForKeyboardId(String text, boolean preserveCase, KeyboardId id) { if (text == null || preserveCase) return text; switch (id.mElementId) { case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: return text.toUpperCase(id.mLocale); default: return text; } } private static int hashCode(Key key) { return Arrays.hashCode(new Object[] { key.mX, Loading java/src/com/android/inputmethod/keyboard/Keyboard.java +8 −1 Original line number Diff line number Diff line Loading @@ -293,6 +293,8 @@ public class Keyboard { public final Set<Key> mShiftLockKeys = new HashSet<Key>(); public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); public KeyboardSet.KeysCache mKeysCache; public int mMostCommonKeyHeight = 0; public int mMostCommonKeyWidth = 0; Loading Loading @@ -361,7 +363,8 @@ public class Keyboard { clearHistogram(); } public void onAddKey(Key key) { public void onAddKey(Key newKey) { final Key key = (mKeysCache != null) ? mKeysCache.get(newKey) : newKey; mKeys.add(key); updateHistogram(key); if (key.mCode == Keyboard.CODE_SHIFT) { Loading Loading @@ -688,6 +691,10 @@ public class Keyboard { params.mTouchPositionCorrection.load(data); } public void setAutoGenerate(KeyboardSet.KeysCache keysCache) { mParams.mKeysCache = keysCache; } public Builder<KP> load(int xmlId, KeyboardId id) { mParams.mId = id; final XmlResourceParser parser = mResources.getXml(xmlId); Loading java/src/com/android/inputmethod/keyboard/KeyboardSet.java +35 −10 Original line number Diff line number Diff line Loading @@ -57,6 +57,25 @@ public class KeyboardSet { private final Context mContext; private final Params mParams; private final KeysCache mKeysCache = new KeysCache(); public static class KeysCache { private final Map<Key, Key> mMap; public KeysCache() { mMap = new HashMap<Key, Key>(); } public Key get(Key key) { final Key existingKey = mMap.get(key); if (existingKey != null) { // Reuse the existing element that equals to "key" without adding "key" to the map. return existingKey; } mMap.put(key, key); return key; } } static class KeyboardElement { final int mElementId; Loading Loading @@ -99,15 +118,15 @@ public class KeyboardSet { } public Keyboard getMainKeyboard() { return getKeyboard(false, false); return getKeyboard(false, false, false); } public Keyboard getSymbolsKeyboard() { return getKeyboard(true, false); return getKeyboard(true, false, false); } public Keyboard getSymbolsShiftedKeyboard() { final Keyboard keyboard = getKeyboard(true, true); final Keyboard keyboard = getKeyboard(true, false, true); // TODO: Remove this logic once we introduce initial keyboard shift state attribute. // Symbol shift keyboard may have a shift key that has a caps lock style indicator (a.k.a. // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked() Loading @@ -116,22 +135,23 @@ public class KeyboardSet { return keyboard; } private Keyboard getKeyboard(boolean isSymbols, boolean isShift) { final int elementId = KeyboardSet.getElementId(mParams.mMode, isSymbols, isShift); private Keyboard getKeyboard(boolean isSymbols, boolean isShiftLock, boolean isShift) { final int elementId = KeyboardSet.getElementId( mParams.mMode, isSymbols, isShiftLock, isShift); final KeyboardElement keyboardElement = mParams.mElementKeyboards.get(elementId); // TODO: If keyboardElement.mAutoGenerate is true, the keyboard will be auto generated // based on keyboardElement.mKayoutId Keyboard XML definition. final KeyboardId id = KeyboardSet.getKeyboardId(elementId, isSymbols, mParams); final Keyboard keyboard = getKeyboard(mContext, keyboardElement.mLayoutId, id); final Keyboard keyboard = getKeyboard(mContext, keyboardElement, id); return keyboard; } public KeyboardId getMainKeyboardId() { final int elementId = KeyboardSet.getElementId(mParams.mMode, false, false); final int elementId = KeyboardSet.getElementId(mParams.mMode, false, false, false); return KeyboardSet.getKeyboardId(elementId, false, mParams); } private Keyboard getKeyboard(Context context, int xmlId, KeyboardId id) { private Keyboard getKeyboard(Context context, KeyboardElement element, KeyboardId id) { final Resources res = context.getResources(); final SoftReference<Keyboard> ref = sKeyboardCache.get(id); Keyboard keyboard = (ref == null) ? null : ref.get(); Loading @@ -140,7 +160,10 @@ public class KeyboardSet { try { final Keyboard.Builder<Keyboard.Params> builder = new Keyboard.Builder<Keyboard.Params>(context, new Keyboard.Params()); builder.load(xmlId, id); if (element.mAutoGenerate) { builder.setAutoGenerate(mKeysCache); } builder.load(element.mLayoutId, id); builder.setTouchPositionCorrectionEnabled(mParams.mTouchPositionCorrectionEnabled); keyboard = builder.build(); } finally { Loading @@ -162,7 +185,8 @@ public class KeyboardSet { return keyboard; } private static int getElementId(int mode, boolean isSymbols, boolean isShift) { private static int getElementId(int mode, boolean isSymbols, boolean isShiftLock, boolean isShift) { switch (mode) { case KeyboardId.MODE_PHONE: return (isSymbols && isShift) Loading @@ -174,6 +198,7 @@ public class KeyboardSet { return isShift ? KeyboardId.ELEMENT_SYMBOLS_SHIFTED : KeyboardId.ELEMENT_SYMBOLS; } // TODO: Consult isShiftLock and isShift to determine the element. return KeyboardId.ELEMENT_ALPHABET; } } Loading Loading
java/res/values/attrs.xml +3 −0 Original line number Diff line number Diff line Loading @@ -242,6 +242,9 @@ <flag name="withIconLeft" value="0x1000" /> <flag name="withIconRight" value="0x2000" /> <flag name="autoXScale" value="0x4000" /> <!-- If true, character case of code, altCode, moreKeys, keyOutputText, keyLabel, or keyHintLabel will never be subject to change. --> <flag name="preserveCase" value="0x8000" /> </attr> <!-- The icon to display on the key instead of the label. --> <attr name="keyIcon" format="enum"> Loading
java/res/xml/keyboard_set.xml +2 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,8 @@ latin:keyboardLocale="en_GB,en_US"> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_qwerty" /> latin:elementKeyboard="@xml/kbd_qwerty" latin:elementAutoGenerate="true" /> <Element latin:elementName="alphabetManualShifted" latin:elementKeyboard="@xml/kbd_qwerty" Loading
java/src/com/android/inputmethod/keyboard/Key.java +56 −20 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ public class Key { private static final int LABEL_FLAGS_WITH_ICON_LEFT = 0x1000; private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000; private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000; private static final int LABEL_FLAGS_PRESERVE_CASE = 0x8000; /** Icon to display instead of a label. Icon takes precedence over a label */ private final int mIconAttrId; Loading Loading @@ -262,19 +263,6 @@ public class Key { // Update row to have current x coordinate. row.setXPos(keyXPos + keyWidth); final String[] moreKeys = style.getStringArray(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. if (params.mId.isAlphabetKeyboard() && !res.getBoolean(R.bool.config_digit_more_keys_enabled)) { mMoreKeys = MoreKeySpecParser.filterOut(res, moreKeys, MoreKeySpecParser.DIGIT_FILTER); } else { mMoreKeys = moreKeys; } mMaxMoreKeysColumn = style.getInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMiniKeyboardColumn); mBackgroundType = style.getInt(keyAttr, R.styleable.Keyboard_Key_backgroundType, BACKGROUND_TYPE_NORMAL); mActionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags, 0); Loading @@ -292,14 +280,39 @@ public class Key { mDisabledIconAttrId = KeyboardIconsSet.getIconAttrId(style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconDisabled, KeyboardIconsSet.ICON_UNDEFINED)); mLabel = style.getString(keyAttr, R.styleable.Keyboard_Key_keyLabel); mHintLabel = style.getString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags, 0); mOutputText = style.getString(keyAttr, R.styleable.Keyboard_Key_keyOutputText); final boolean preserveCase = (mLabelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0; final String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); if (moreKeys != null) { for (int i = 0; i < moreKeys.length; i++) { moreKeys[i] = adjustCaseOfStringForKeyboardId( moreKeys[i], preserveCase, params.mId); } } // TODO: Add new key label flag to control this. // In Arabic symbol layouts, we'd like to keep digits in more keys regardless of // config_digit_more_keys_enabled. if (params.mId.isAlphabetKeyboard() && !res.getBoolean(R.bool.config_digit_more_keys_enabled)) { mMoreKeys = MoreKeySpecParser.filterOut(res, moreKeys, MoreKeySpecParser.DIGIT_FILTER); } else { mMoreKeys = moreKeys; } mMaxMoreKeysColumn = style.getInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMiniKeyboardColumn); mLabel = adjustCaseOfStringForKeyboardId(style.getString( keyAttr, R.styleable.Keyboard_Key_keyLabel), preserveCase, params.mId); mHintLabel = adjustCaseOfStringForKeyboardId(style.getString( keyAttr, R.styleable.Keyboard_Key_keyHintLabel), preserveCase, params.mId); mOutputText = adjustCaseOfStringForKeyboardId(style.getString( keyAttr, R.styleable.Keyboard_Key_keyOutputText), preserveCase, params.mId); // Choose the first letter of the label as primary code if not // specified. final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code, Keyboard.CODE_UNSPECIFIED); final int code = adjustCaseOfCodeForKeyboardId(style.getInt( keyAttr, R.styleable.Keyboard_Key_code, Keyboard.CODE_UNSPECIFIED), preserveCase, params.mId); if (code == Keyboard.CODE_UNSPECIFIED && mOutputText == null && !TextUtils.isEmpty(mLabel)) { if (mLabel.length() != 1) { Loading @@ -312,13 +325,36 @@ public class Key { } else { mCode = code; } mAltCode = style.getInt(keyAttr, R.styleable.Keyboard_Key_altCode, Keyboard.CODE_UNSPECIFIED); mAltCode = adjustCaseOfCodeForKeyboardId(style.getInt(keyAttr, R.styleable.Keyboard_Key_altCode, Keyboard.CODE_UNSPECIFIED), preserveCase, params.mId); mHashCode = hashCode(this); keyAttr.recycle(); } private static int adjustCaseOfCodeForKeyboardId(int code, boolean preserveCase, KeyboardId id) { if (!Keyboard.isLetterCode(code) || preserveCase) return code; final String text = new String(new int[] { code } , 0, 1); final String casedText = adjustCaseOfStringForKeyboardId(text, preserveCase, id); return casedText.codePointAt(0); } private static String adjustCaseOfStringForKeyboardId(String text, boolean preserveCase, KeyboardId id) { if (text == null || preserveCase) return text; switch (id.mElementId) { case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: return text.toUpperCase(id.mLocale); default: return text; } } private static int hashCode(Key key) { return Arrays.hashCode(new Object[] { key.mX, Loading
java/src/com/android/inputmethod/keyboard/Keyboard.java +8 −1 Original line number Diff line number Diff line Loading @@ -293,6 +293,8 @@ public class Keyboard { public final Set<Key> mShiftLockKeys = new HashSet<Key>(); public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); public KeyboardSet.KeysCache mKeysCache; public int mMostCommonKeyHeight = 0; public int mMostCommonKeyWidth = 0; Loading Loading @@ -361,7 +363,8 @@ public class Keyboard { clearHistogram(); } public void onAddKey(Key key) { public void onAddKey(Key newKey) { final Key key = (mKeysCache != null) ? mKeysCache.get(newKey) : newKey; mKeys.add(key); updateHistogram(key); if (key.mCode == Keyboard.CODE_SHIFT) { Loading Loading @@ -688,6 +691,10 @@ public class Keyboard { params.mTouchPositionCorrection.load(data); } public void setAutoGenerate(KeyboardSet.KeysCache keysCache) { mParams.mKeysCache = keysCache; } public Builder<KP> load(int xmlId, KeyboardId id) { mParams.mId = id; final XmlResourceParser parser = mResources.getXml(xmlId); Loading
java/src/com/android/inputmethod/keyboard/KeyboardSet.java +35 −10 Original line number Diff line number Diff line Loading @@ -57,6 +57,25 @@ public class KeyboardSet { private final Context mContext; private final Params mParams; private final KeysCache mKeysCache = new KeysCache(); public static class KeysCache { private final Map<Key, Key> mMap; public KeysCache() { mMap = new HashMap<Key, Key>(); } public Key get(Key key) { final Key existingKey = mMap.get(key); if (existingKey != null) { // Reuse the existing element that equals to "key" without adding "key" to the map. return existingKey; } mMap.put(key, key); return key; } } static class KeyboardElement { final int mElementId; Loading Loading @@ -99,15 +118,15 @@ public class KeyboardSet { } public Keyboard getMainKeyboard() { return getKeyboard(false, false); return getKeyboard(false, false, false); } public Keyboard getSymbolsKeyboard() { return getKeyboard(true, false); return getKeyboard(true, false, false); } public Keyboard getSymbolsShiftedKeyboard() { final Keyboard keyboard = getKeyboard(true, true); final Keyboard keyboard = getKeyboard(true, false, true); // TODO: Remove this logic once we introduce initial keyboard shift state attribute. // Symbol shift keyboard may have a shift key that has a caps lock style indicator (a.k.a. // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked() Loading @@ -116,22 +135,23 @@ public class KeyboardSet { return keyboard; } private Keyboard getKeyboard(boolean isSymbols, boolean isShift) { final int elementId = KeyboardSet.getElementId(mParams.mMode, isSymbols, isShift); private Keyboard getKeyboard(boolean isSymbols, boolean isShiftLock, boolean isShift) { final int elementId = KeyboardSet.getElementId( mParams.mMode, isSymbols, isShiftLock, isShift); final KeyboardElement keyboardElement = mParams.mElementKeyboards.get(elementId); // TODO: If keyboardElement.mAutoGenerate is true, the keyboard will be auto generated // based on keyboardElement.mKayoutId Keyboard XML definition. final KeyboardId id = KeyboardSet.getKeyboardId(elementId, isSymbols, mParams); final Keyboard keyboard = getKeyboard(mContext, keyboardElement.mLayoutId, id); final Keyboard keyboard = getKeyboard(mContext, keyboardElement, id); return keyboard; } public KeyboardId getMainKeyboardId() { final int elementId = KeyboardSet.getElementId(mParams.mMode, false, false); final int elementId = KeyboardSet.getElementId(mParams.mMode, false, false, false); return KeyboardSet.getKeyboardId(elementId, false, mParams); } private Keyboard getKeyboard(Context context, int xmlId, KeyboardId id) { private Keyboard getKeyboard(Context context, KeyboardElement element, KeyboardId id) { final Resources res = context.getResources(); final SoftReference<Keyboard> ref = sKeyboardCache.get(id); Keyboard keyboard = (ref == null) ? null : ref.get(); Loading @@ -140,7 +160,10 @@ public class KeyboardSet { try { final Keyboard.Builder<Keyboard.Params> builder = new Keyboard.Builder<Keyboard.Params>(context, new Keyboard.Params()); builder.load(xmlId, id); if (element.mAutoGenerate) { builder.setAutoGenerate(mKeysCache); } builder.load(element.mLayoutId, id); builder.setTouchPositionCorrectionEnabled(mParams.mTouchPositionCorrectionEnabled); keyboard = builder.build(); } finally { Loading @@ -162,7 +185,8 @@ public class KeyboardSet { return keyboard; } private static int getElementId(int mode, boolean isSymbols, boolean isShift) { private static int getElementId(int mode, boolean isSymbols, boolean isShiftLock, boolean isShift) { switch (mode) { case KeyboardId.MODE_PHONE: return (isSymbols && isShift) Loading @@ -174,6 +198,7 @@ public class KeyboardSet { return isShift ? KeyboardId.ELEMENT_SYMBOLS_SHIFTED : KeyboardId.ELEMENT_SYMBOLS; } // TODO: Consult isShiftLock and isShift to determine the element. return KeyboardId.ELEMENT_ALPHABET; } } Loading