Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09f8b126 authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka
Browse files

Add Key preserveCase enum to keyLabelOptions attribute

To support auto generate key depending keyboard element id, the
KeysCache class is introduced to hold whole keys and reuse.

Change-Id: Icb81b5f1c1b3aaa31968dcdb93aa0a856e737f78
parent bcf2b793
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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">
+2 −1
Original line number Diff line number Diff line
@@ -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"
+56 −20
Original line number Diff line number Diff line
@@ -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;
@@ -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);
@@ -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) {
@@ -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,
+8 −1
Original line number Diff line number Diff line
@@ -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;

@@ -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) {
@@ -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);
+35 −10
Original line number Diff line number Diff line
@@ -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;
@@ -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()
@@ -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();
@@ -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 {
@@ -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)
@@ -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;
        }
    }