Keyboard.java revision 1e4684ad24914cde713980bb4174810bbf8bae10
15a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka/*
28632bff2d5a8e1160989008dea6eff4b94b065ddTadashi G. Takaoka * Copyright (C) 2010 The Android Open Source Project
35a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *
45a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); you may not
55a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * use this file except in compliance with the License. You may obtain a copy of
65a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * the License at
75a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *
85a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0
95a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *
105a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
115a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
125a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
135a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * License for the specific language governing permissions and limitations under
145a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * the License.
155a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka */
165a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
175a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
185a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.content.Context;
204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.content.res.Resources;
214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.content.res.TypedArray;
224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.content.res.XmlResourceParser;
23d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaokaimport android.text.TextUtils;
244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.util.DisplayMetrics;
257dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaokaimport android.util.Log;
264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.util.TypedValue;
274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.util.Xml;
284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.view.InflateException;
295a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport com.android.inputmethod.compat.EditorInfoCompatUtils;
314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyStyles;
32c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
33e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardShiftState;
344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport com.android.inputmethod.latin.XmlParseUtils;
37b7758d6f912093747d4b18fbc8d1dcd77c7d1f9bTadashi G. Takaoka
384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport org.xmlpull.v1.XmlPullParser;
394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport org.xmlpull.v1.XmlPullParserException;
404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport java.io.IOException;
424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport java.util.Arrays;
438da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaokaimport java.util.Collections;
442013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaokaimport java.util.HashMap;
454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport java.util.HashSet;
46167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaokaimport java.util.Map;
47167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaokaimport java.util.Set;
485a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
495a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka/**
505a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
515a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * consists of rows of keys.
525a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
535a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * <pre>
545a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * &lt;Keyboard
555a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         latin:keyWidth="%10p"
565a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         latin:keyHeight="50px"
575a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         latin:horizontalGap="2px"
585a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         latin:verticalGap="2px" &gt;
595a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *     &lt;Row latin:keyWidth="32px" &gt;
605a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         &lt;Key latin:keyLabel="A" /&gt;
615a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         ...
625a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *     &lt;/Row&gt;
635a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *     ...
645a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * &lt;/Keyboard&gt;
655a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * </pre>
665a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka */
675a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapublic class Keyboard {
687dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka    private static final String TAG = Keyboard.class.getSimpleName();
697dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka
70ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka    /** Some common keys code. Must be positive.
71ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka     * These should be aligned with values/keycodes.xml
72ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka     */
73571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_ENTER = '\n';
74571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_TAB = '\t';
75571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_SPACE = ' ';
76571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_PERIOD = '.';
770730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    public static final int CODE_DASH = '-';
780730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    public static final int CODE_SINGLE_QUOTE = '\'';
790730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    public static final int CODE_DOUBLE_QUOTE = '"';
802b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    // TODO: Check how this should work for right-to-left languages. It seems to stand
812b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
822b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    // managed by the font? Or is it a different char?
832b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_PARENTHESIS = ')';
842b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
852b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_CURLY_BRACKET = '}';
862b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
87851c3267d4ab21f892b4164783bb4959c88b44ffTadashi G. Takaoka    public static final int CODE_DIGIT0 = '0';
88851c3267d4ab21f892b4164783bb4959c88b44ffTadashi G. Takaoka    public static final int CODE_PLUS = '+';
898cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka    private static final int MINIMUM_LETTER_CODE = CODE_TAB;
902b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard
918cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka    /** Special keys code. Must be negative.
92ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka     * These should be aligned with values/keycodes.xml
93ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka     */
94571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_SHIFT = -1;
95e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
96d2c5fdda862f6dd2a1e020cf674c35fbbc63fc92Tadashi G. Takaoka    public static final int CODE_CAPSLOCK = -3;
978cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka    public static final int CODE_OUTPUT_TEXT = -4;
98571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_DELETE = -5;
99d2c5fdda862f6dd2a1e020cf674c35fbbc63fc92Tadashi G. Takaoka    public static final int CODE_SETTINGS = -6;
100cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public static final int CODE_SHORTCUT = -7;
101c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka    // Code value representing the code is not specified.
1028cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka    public static final int CODE_UNSPECIFIED = -9;
1035a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
104167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka    public final KeyboardId mId;
10563584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka    public final int mThemeId;
106167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka
107167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka    /** Total height of the keyboard, including the padding and keys */
1088da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mOccupiedHeight;
1098da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    /** Total width of the keyboard, including the padding and keys */
1108da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mOccupiedWidth;
111167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka
1128fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    /** The padding above the keyboard */
1138fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    public final int mTopPadding;
1145a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    /** Default gap between rows */
1158da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mVerticalGap;
1168da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
1178fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    public final int mMostCommonKeyHeight;
1188da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mMostCommonKeyWidth;
1195a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1209d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    /** More keys keyboard template */
1219d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public final int mMoreKeysTemplate;
1229b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka
1239237a72634be821c22911633ef0848130e162d58Tadashi G. Takaoka    /** Maximum column for mini keyboard */
1249237a72634be821c22911633ef0848130e162d58Tadashi G. Takaoka    public final int mMaxMiniKeyboardColumn;
1259b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka
1267139335dcd84209285c028f5d83cb72bfbf1a966Tadashi G. Takaoka    /** True if Right-To-Left keyboard */
1278da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final boolean mIsRtlKeyboard;
1285a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1298da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    /** List of keys and icons in this keyboard */
1304a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    public final Set<Key> mKeys;
1314a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    public final Set<Key> mShiftKeys;
1328da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final Set<Key> mShiftLockKeys;
1338da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final KeyboardIconsSet mIconsSet;
1346fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka
1352013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka    private final Map<Integer, Key> mKeyCache = new HashMap<Integer, Key>();
136c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
1378fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    private final ProximityInfo mProximityInfo;
1388fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
139e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka    // TODO: Remove this variable.
140e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka    private final KeyboardShiftState mShiftState = new KeyboardShiftState();
141c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka
1424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    public Keyboard(Params params) {
1438da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mId = params.mId;
14463584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka        mThemeId = params.mThemeId;
1458da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mOccupiedHeight = params.mOccupiedHeight;
1468da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mOccupiedWidth = params.mOccupiedWidth;
1478fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka        mMostCommonKeyHeight = params.mMostCommonKeyHeight;
1488da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mMostCommonKeyWidth = params.mMostCommonKeyWidth;
1498da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mIsRtlKeyboard = params.mIsRtlKeyboard;
1509d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysTemplate = params.mMoreKeysTemplate;
1519237a72634be821c22911633ef0848130e162d58Tadashi G. Takaoka        mMaxMiniKeyboardColumn = params.mMaxMiniKeyboardColumn;
1528da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
1538fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka        mTopPadding = params.mTopPadding;
1548da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mVerticalGap = params.mVerticalGap;
1558da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
1564a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka        mKeys = Collections.unmodifiableSet(params.mKeys);
1574a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka        mShiftKeys = Collections.unmodifiableSet(params.mShiftKeys);
1588da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mShiftLockKeys = Collections.unmodifiableSet(params.mShiftLockKeys);
1598da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mIconsSet = params.mIconsSet;
1605a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1610d5494c66ac3e5947040e8148091163a1c8716f7satok        mProximityInfo = new ProximityInfo(
1628da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
163294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima                mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection);
1648fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1658fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
166043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard    public ProximityInfo getProximityInfo() {
167043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard        return mProximityInfo;
1685a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
1695a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1702013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka    public Key getKey(int code) {
1718cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka        if (code == CODE_UNSPECIFIED) {
172623d0155b6a316fdc9335370cdd4005bbb474ef3Tadashi G. Takaoka            return null;
173623d0155b6a316fdc9335370cdd4005bbb474ef3Tadashi G. Takaoka        }
1742013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        final Integer keyCode = code;
1752013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (mKeyCache.containsKey(keyCode)) {
1762013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            return mKeyCache.get(keyCode);
1772013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
1782013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
1792013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        for (final Key key : mKeys) {
1802013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            if (key.mCode == code) {
1812013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                mKeyCache.put(keyCode, key);
1822013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                return key;
1832013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
1842013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
1852013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        mKeyCache.put(keyCode, null);
1862013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        return null;
1872013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka    }
1882013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
189c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method.
1904a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    boolean hasShiftLockKey() {
1916d9bcd5e1317722207116ab6a3ddfcb152005701Tadashi G. Takaoka        return !mShiftLockKeys.isEmpty();
192571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
193571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
194c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method.
1954a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    void setShiftLocked(boolean newShiftLockState) {
1966d9bcd5e1317722207116ab6a3ddfcb152005701Tadashi G. Takaoka        for (final Key key : mShiftLockKeys) {
1976d9bcd5e1317722207116ab6a3ddfcb152005701Tadashi G. Takaoka            // To represent "shift locked" state. The highlight is handled by background image that
1986d9bcd5e1317722207116ab6a3ddfcb152005701Tadashi G. Takaoka            // might be a StateListDrawable.
199e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka            key.setHighlightOn(newShiftLockState);
20042fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka            final int attrId = newShiftLockState
20142fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka                    ? R.styleable.Keyboard_iconShiftKeyShifted
20242fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka                    : R.styleable.Keyboard_iconShiftKey;
20342fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka            key.setIcon(mIconsSet.getIconByAttrId(attrId));
204571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        }
205e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        mShiftState.setShiftLocked(newShiftLockState);
206571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
207571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
208c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Move this method to KeyboardId.
209571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public boolean isShiftLocked() {
210e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        return mShiftState.isShiftLocked();
211571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
212571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
21307145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka    private void setShiftKeyGraphics(boolean newShiftState) {
21407145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka        if (mShiftState.isShiftLocked()) {
21507145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka            return;
21607145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka        }
21707145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka        for (final Key key : mShiftKeys) {
21807145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka            final int attrId = newShiftState
21907145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka                    ? R.styleable.Keyboard_iconShiftKeyShifted
22007145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka                    : R.styleable.Keyboard_iconShiftKey;
22107145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka            key.setIcon(mIconsSet.getIconByAttrId(attrId));
22207145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka        }
22307145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka    }
22407145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka
225c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method.
2264a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    void setShifted(boolean newShiftState) {
22707145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka        setShiftKeyGraphics(newShiftState);
228e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        mShiftState.setShifted(newShiftState);
2295a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
2305a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
231c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Move this method to KeyboardId.
2325a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    public boolean isShiftedOrShiftLocked() {
233e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        return mShiftState.isShiftedOrShiftLocked();
2345a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
2355a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
236c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method
2374a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    void setAutomaticTemporaryUpperCase() {
23807145a3706d7692806b9c53548795fa2dbf8f4f0Tadashi G. Takaoka        setShiftKeyGraphics(true);
239e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        mShiftState.setAutomaticTemporaryUpperCase();
240571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
241571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
242c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Move this method to KeyboardId.
243571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public boolean isManualTemporaryUpperCase() {
244e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        return mShiftState.isManualTemporaryUpperCase();
245571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
246571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
247c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method.
248d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka    public CharSequence adjustLabelCase(CharSequence label) {
2491ebedd7a15a8fd94e68fb43eb089ed87c4c5a480Tadashi G. Takaoka        if (mId.isAlphabetKeyboard() && isShiftedOrShiftLocked() && !TextUtils.isEmpty(label)
2501ebedd7a15a8fd94e68fb43eb089ed87c4c5a480Tadashi G. Takaoka                && label.length() < 3 && Character.isLowerCase(label.charAt(0))) {
251d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka            return label.toString().toUpperCase(mId.mLocale);
252d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka        }
253d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka        return label;
254571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
255571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
256ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka    public static boolean isLetterCode(int code) {
2578cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka        return code >= MINIMUM_LETTER_CODE;
258ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka    }
259ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka
2604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    public static class Params {
2614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public KeyboardId mId;
2624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mThemeId;
2634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        /** Total height and width of the keyboard, including the paddings and keys */
2654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mOccupiedHeight;
2664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mOccupiedWidth;
2674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        /** Base height and width of the keyboard used to calculate rows' or keys' heights and
2694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         *  widths
2704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         */
2714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mBaseHeight;
2724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mBaseWidth;
2734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mTopPadding;
2754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mBottomPadding;
2764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mHorizontalEdgesPadding;
2774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mHorizontalCenterPadding;
2784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mDefaultRowHeight;
2804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mDefaultKeyWidth;
2814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mHorizontalGap;
2824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mVerticalGap;
2834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public boolean mIsRtlKeyboard;
2854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMoreKeysTemplate;
2864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMaxMiniKeyboardColumn;
2874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int GRID_WIDTH;
2894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int GRID_HEIGHT;
2904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final Set<Key> mKeys = new HashSet<Key>();
2924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final Set<Key> mShiftKeys = new HashSet<Key>();
2934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final Set<Key> mShiftLockKeys = new HashSet<Key>();
2944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
2954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMostCommonKeyHeight = 0;
2974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMostCommonKeyWidth = 0;
2984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final TouchPositionCorrection mTouchPositionCorrection =
3004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                new TouchPositionCorrection();
3014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static class TouchPositionCorrection {
3034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3;
3044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public boolean mEnabled;
3064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float[] mXs;
3074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float[] mYs;
3084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float[] mRadii;
3094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void load(String[] data) {
3114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int dataLength = data.length;
3124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
3134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (LatinImeLogger.sDBG)
3144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new RuntimeException(
3154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "the size of touch position correction data is invalid");
3164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return;
3174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
3184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
3204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mXs = new float[length];
3214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mYs = new float[length];
3224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRadii = new float[length];
3234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
3244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    for (int i = 0; i < dataLength; ++i) {
3254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE;
3264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
3274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        final float value = Float.parseFloat(data[i]);
3284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (type == 0) {
3294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mXs[index] = value;
3304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        } else if (type == 1) {
3314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mYs[index] = value;
3324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        } else {
3334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mRadii[index] = value;
3344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        }
3354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
3364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } catch (NumberFormatException e) {
3374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (LatinImeLogger.sDBG) {
3384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new RuntimeException(
3394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "the number format for touch position correction data is invalid");
3404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
3414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mXs = null;
3424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mYs = null;
3434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mRadii = null;
3444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
3454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void setEnabled(boolean enabled) {
3484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mEnabled = enabled;
3494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public boolean isValid() {
3524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mEnabled && mXs != null && mYs != null && mRadii != null
3534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
3544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected void clearKeys() {
3584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mKeys.clear();
3594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mShiftKeys.clear();
3604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mShiftLockKeys.clear();
3614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            clearHistogram();
3624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public void onAddKey(Key key) {
3654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mKeys.add(key);
3664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            updateHistogram(key);
3674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (key.mCode == Keyboard.CODE_SHIFT) {
3684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mShiftKeys.add(key);
3694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (key.isSticky()) {
3704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mShiftLockKeys.add(key);
3714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
3724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private int mMaxHeightCount = 0;
3764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private int mMaxWidthCount = 0;
3774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final Map<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>();
3784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final Map<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>();
3794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void clearHistogram() {
3814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMostCommonKeyHeight = 0;
3824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMaxHeightCount = 0;
3834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mHeightHistogram.clear();
3844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMaxWidthCount = 0;
3864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMostCommonKeyWidth = 0;
3874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mWidthHistogram.clear();
3884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static int updateHistogramCounter(Map<Integer, Integer> histogram, Integer key) {
3914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1;
3924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            histogram.put(key, count);
3934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return count;
3944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void updateHistogram(Key key) {
3974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final Integer height = key.mHeight + key.mVerticalGap;
3984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int heightCount = updateHistogramCounter(mHeightHistogram, height);
3994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (heightCount > mMaxHeightCount) {
4004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMaxHeightCount = heightCount;
4014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMostCommonKeyHeight = height;
4024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
4034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final Integer width = key.mWidth + key.mHorizontalGap;
4054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int widthCount = updateHistogramCounter(mWidthHistogram, width);
4064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (widthCount > mMaxWidthCount) {
4074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMaxWidthCount = widthCount;
4084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMostCommonKeyWidth = width;
4094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
4104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
4114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    }
4124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4135a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    /**
4140c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka     * Returns the array of the keys that are closest to the given point.
4155a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * @param x the x-coordinate of the point
4165a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * @param y the y-coordinate of the point
4170c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka     * @return the array of the nearest keys to the given point. If the given
4185a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * point is out of range, then an array of size zero is returned.
4195a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     */
4200c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka    public Key[] getNearestKeys(int x, int y) {
4214f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        // Avoid dead pixels at edges of the keyboard
4224f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1));
4234f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
4244f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
4255a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
42663584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka
4277dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka    public static String printableCode(int code) {
4287dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        switch (code) {
4297dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_SHIFT: return "shift";
4307dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_SWITCH_ALPHA_SYMBOL: return "symbol";
4317dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_CAPSLOCK: return "capslock";
4328cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka        case CODE_OUTPUT_TEXT: return "text";
4337dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_DELETE: return "delete";
4347dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_SHORTCUT: return "shortcut";
4357dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_UNSPECIFIED: return "unspec";
4367dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        default:
4372a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (code <= 0) Log.w(TAG, "Unknown non-positive key code=" + code);
4382a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (code < CODE_SPACE) return String.format("'\\u%02x'", code);
4392a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (code < 0x100) return String.format("'%c'", code);
4402a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            return String.format("'\\u%04x'", code);
4417dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        }
4427dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka    }
4434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4444087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka    public static String toThemeName(int themeId) {
4454087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        // This should be aligned with theme-*.xml resource files' themeId attribute.
4464087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        switch (themeId) {
4474087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 0: return "Basic";
4484087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 1: return "BasicHighContrast";
4494087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 5: return "IceCreamSandwich";
4504087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 6: return "Stone";
4514087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 7: return "StoneBold";
4524087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 8: return "GingerBread";
4534087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        default: return null;
4544087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        }
4554087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka    }
4564087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka
4574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    /**
4584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * Keyboard Building helper.
4594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *
4604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * This class parses Keyboard XML file and eventually build a Keyboard.
4614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * The Keyboard XML file looks like:
4624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/keyboard.xml --&lt;
4644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;Keyboard keyboard_attributes*&lt;
4654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;!-- Keyboard Content --&lt;
4664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Row row_attributes*&lt;
4674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Row Content --&lt;
4684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Key key_attributes* /&lt;
4694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Spacer horizontalGap="0.2in" /&lt;
4704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;include keyboardLayout="@xml/other_keys"&lt;
4714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       ...
4724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/Row&lt;
4734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;include keyboardLayout="@xml/other_rows"&lt;
4744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/Keyboard&lt;
4764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * The XML file which is included in other file must have &gt;merge&lt; as root element,
4784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * such as:
4794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/other_keys.xml --&lt;
4814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;merge&lt;
4824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Key key_attributes* /&lt;
4834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/merge&lt;
4854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * and
4874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/other_rows.xml --&lt;
4894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;merge&lt;
4904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Row row_attributes*&lt;
4914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Key key_attributes* /&lt;
4924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/Row&lt;
4934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/merge&lt;
4954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * You can also use switch-case-default tags to select Rows and Keys.
4974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;switch&lt;
4994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;case case_attribute*&lt;
5004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Any valid tags at switch position --&lt;
5014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/case&lt;
5024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
5034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;default&lt;
5044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Any valid tags at switch position --&lt;
5054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/default&lt;
5064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/switch&lt;
5074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
5084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * You can declare Key style and specify styles within Key tags.
5094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
5104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;switch&lt;
5114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;case mode="email"&lt;
5124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
5134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *           keyLabel=".com"
5144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         /&lt;
5154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;/case&lt;
5164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;case mode="url"&lt;
5174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
5184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *           keyLabel="http://"
5194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         /&lt;
5204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;/case&lt;
5214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/switch&lt;
5224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
5234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Key keyStyle="shift-key" ... /&lt;
5244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
5254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     */
5264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    public static class Builder<KP extends Params> {
5284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG = Builder.class.getSimpleName();
5294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final boolean DEBUG = false;
5304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        // Keyboard XML Tags
5324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_KEYBOARD = "Keyboard";
5334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_ROW = "Row";
5344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_KEY = "Key";
5354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_SPACER = "Spacer";
5364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_INCLUDE = "include";
5374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_MERGE = "merge";
5384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_SWITCH = "switch";
5394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_CASE = "case";
5404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_DEFAULT = "default";
5414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static final String TAG_KEY_STYLE = "key-style";
5424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
5444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final int DEFAULT_KEYBOARD_ROWS = 4;
5454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final KP mParams;
5474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final Context mContext;
5484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final Resources mResources;
5494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final DisplayMetrics mDisplayMetrics;
5504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private int mCurrentY = 0;
5524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Row mCurrentRow = null;
5534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean mLeftEdge;
5544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean mTopEdge;
5554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Key mRightEdgeKey = null;
5564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final KeyStyles mKeyStyles = new KeyStyles();
5574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        /**
5594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
5604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
5614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * defines.
5624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         */
5634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static class Row {
5644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // keyWidth enum constants
5654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_NOT_ENUM = 0;
5664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_FILL_RIGHT = -1;
5674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_FILL_BOTH = -2;
5684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private final Params mParams;
5704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            /** Default width of a key in this row. */
5714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public final float mDefaultKeyWidth;
5724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            /** Default height of a key in this row. */
5734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public final int mRowHeight;
5744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private final int mCurrentY;
5764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // Will be updated by {@link Key}'s constructor.
5774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private float mCurrentX;
5784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public Row(Resources res, Params params, XmlPullParser parser, int y) {
5804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mParams = params;
5814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
5824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard);
5834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRowHeight = (int)Builder.getDimensionOrFraction(keyboardAttr,
5844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_rowHeight,
5854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseHeight, params.mDefaultRowHeight);
5864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyboardAttr.recycle();
5874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
5884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key);
5894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mDefaultKeyWidth = Builder.getDimensionOrFraction(keyAttr,
5904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth,
5914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseWidth, params.mDefaultKeyWidth);
5924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttr.recycle();
5934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentY = y;
5954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX = 0.0f;
5964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void setXPos(float keyXPos) {
5994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX = keyXPos;
6004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void advanceXPos(float width) {
6034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX += width;
6044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public int getKeyY() {
6074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mCurrentY;
6084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float getKeyX(TypedArray keyAttr) {
6114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int widthType = Builder.getEnumValue(keyAttr,
6124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
6134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (widthType == KEYWIDTH_FILL_BOTH) {
6144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillBoth, the key width should start right after the nearest
6154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // key on the left hand side.
6164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return mCurrentX;
6174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
6184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int keyboardRightEdge = mParams.mOccupiedWidth
6204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - mParams.mHorizontalEdgesPadding;
6214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
6224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final float keyXPos = Builder.getDimensionOrFraction(keyAttr,
6234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0);
6244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (keyXPos < 0) {
6254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // If keyXPos is negative, the actual x-coordinate will be
6264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // keyboardWidth + keyXPos.
6274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // keyXPos shouldn't be less than mCurrentX because drawable area for this
6284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // key starts at mCurrentX. Or, this key will overlaps the adjacent key on
6294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // its left hand side.
6304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
6314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
6324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        return keyXPos + mParams.mHorizontalEdgesPadding;
6334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
6344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
6354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mCurrentX;
6364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float getKeyWidth(TypedArray keyAttr, float keyXPos) {
6394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int widthType = Builder.getEnumValue(keyAttr,
6404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
6414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                switch (widthType) {
6424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                case KEYWIDTH_FILL_RIGHT:
6434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                case KEYWIDTH_FILL_BOTH:
6444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final int keyboardRightEdge =
6454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
6464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillRight, the actual key width will be determined to fill
6474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // out the area up to the right edge of the keyboard.
6484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillBoth, the actual key width will be determined to fill out
6494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // the area between the nearest key on the left hand side and the right edge of
6504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // the keyboard.
6514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return keyboardRightEdge - keyXPos;
6524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                default: // KEYWIDTH_NOT_ENUM
6534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return Builder.getDimensionOrFraction(keyAttr,
6544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Key_keyWidth,
6554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mParams.mBaseWidth, mDefaultKeyWidth);
6564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
6574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Builder(Context context, KP params) {
6614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mContext = context;
6624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final Resources res = context.getResources();
6634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mResources = res;
6644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mDisplayMetrics = res.getDisplayMetrics();
6654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams = params;
6674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            setTouchPositionCorrectionData(context, params);
6694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
6714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
6724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static void setTouchPositionCorrectionData(Context context, Params params) {
6754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = context.obtainStyledAttributes(
6764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    null, R.styleable.Keyboard, R.attr.keyboardStyle, 0);
6774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.mThemeId = a.getInt(R.styleable.Keyboard_themeId, 0);
6784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int resourceId = a.getResourceId(
6794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_touchPositionCorrectionData, 0);
6804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            a.recycle();
6814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (resourceId == 0) {
6824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (LatinImeLogger.sDBG)
6831e4684ad24914cde713980bb4174810bbf8bae10Jean Chalard                    Log.e(TAG, "touchPositionCorrectionData is not defined");
6844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return;
6854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final String[] data = context.getResources().getStringArray(resourceId);
6884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.mTouchPositionCorrection.load(data);
6894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Builder<KP> load(int xmlId, KeyboardId id) {
6924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.mId = id;
6934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final XmlResourceParser parser = mResources.getXml(xmlId);
6944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
6954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboard(parser);
6964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } catch (XmlPullParserException e) {
6974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                Log.w(TAG, "keyboard XML parse error: " + e);
6984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new IllegalArgumentException(e);
6994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } catch (IOException e) {
7004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                Log.w(TAG, "keyboard XML parse error: " + e);
7014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new RuntimeException(e);
7024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
7034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parser.close();
7044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
7054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return this;
7064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public void setTouchPositionCorrectionEnabled(boolean enabled) {
7094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.mTouchPositionCorrection.setEnabled(enabled);
7104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Keyboard build() {
7134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return new Keyboard(mParams);
7144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboard(XmlPullParser parser)
7174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
7184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId));
7194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
7204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
7214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
7224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
7234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEYBOARD.equals(tag)) {
7244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyboardAttributes(parser);
7254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        startKeyboard();
7264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyboardContent(parser, false);
7274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
7284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
7294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD);
7304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
7314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
7324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
7334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboardAttributes(XmlPullParser parser) {
7364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int displayWidth = mDisplayMetrics.widthPixels;
7374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
7384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
7394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.style.Keyboard);
7404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
7414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Key);
7424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
7434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int displayHeight = mDisplayMetrics.heightPixels;
7444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int keyboardHeight = (int)keyboardAttr.getDimension(
7454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
7464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int maxKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
7474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
7484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                int minKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
7494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2);
7504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (minKeyboardHeight < 0) {
7514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // Specified fraction was negative, so it should be calculated against display
7524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // width.
7534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    minKeyboardHeight = -(int)getDimensionOrFraction(keyboardAttr,
7544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
7554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
7564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Params params = mParams;
7574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Keyboard height will not exceed maxKeyboardHeight and will not be less than
7584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // minKeyboardHeight.
7594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mOccupiedHeight = Math.max(
7604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
7614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mOccupiedWidth = params.mId.mWidth;
7624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mTopPadding = (int)getDimensionOrFraction(keyboardAttr,
7634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0);
7644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr,
7654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0);
7664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mHorizontalEdgesPadding = (int)getDimensionOrFraction(keyboardAttr,
7674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardHorizontalEdgesPadding,
7684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        mParams.mOccupiedWidth, 0);
7694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2
7714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - params.mHorizontalCenterPadding;
7724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mDefaultKeyWidth = (int)getDimensionOrFraction(keyAttr,
7734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth,
7744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS);
7754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr,
7764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0);
7774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr,
7784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0);
7794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding
7804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - params.mBottomPadding + params.mVerticalGap;
7814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr,
7824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_rowHeight, params.mBaseHeight,
7834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
7844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mIsRtlKeyboard = keyboardAttr.getBoolean(
7864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_isRtlKeyboard, false);
7874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mMoreKeysTemplate = keyboardAttr.getResourceId(
7884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_moreKeysTemplate, 0);
7894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mMaxMiniKeyboardColumn = keyAttr.getInt(
7904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
7914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mIconsSet.loadIcons(keyboardAttr);
7934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
7944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttr.recycle();
7954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyboardAttr.recycle();
7964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
7974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboardContent(XmlPullParser parser, boolean skip)
8004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
8024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
8034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
8044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_ROW.equals(tag)) {
8064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Row row = parseRowAttributes(parser);
8074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW));
8084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (!skip)
8094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            startRow(row);
8104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseRowContent(parser, row, skip);
8114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_INCLUDE.equals(tag)) {
8124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseIncludeKeyboardContent(parser, skip);
8134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SWITCH.equals(tag)) {
8144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSwitchKeyboardContent(parser, skip);
8154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
8164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyStyle(parser, skip);
8174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW);
8194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
8214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEYBOARD.equals(tag)) {
8234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        endKeyboard();
8244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
8264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            || TAG_MERGE.equals(tag)) {
8274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
8284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
8304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        continue;
8314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
8334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
8354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Row parseRowAttributes(XmlPullParser parser) throws XmlPullParserException {
8394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
8404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard);
8414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
8424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (a.hasValue(R.styleable.Keyboard_horizontalGap))
8434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
8444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (a.hasValue(R.styleable.Keyboard_verticalGap))
8454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
8464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return new Row(mResources, mParams, parser, mCurrentY);
8474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
8484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                a.recycle();
8494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseRowContent(XmlPullParser parser, Row row, boolean skip)
8534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
8554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
8564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
8574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEY.equals(tag)) {
8594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKey(parser, row, skip);
8604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SPACER.equals(tag)) {
8614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSpacer(parser, row, skip);
8624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_INCLUDE.equals(tag)) {
8634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseIncludeRowContent(parser, row, skip);
8644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SWITCH.equals(tag)) {
8654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSwitchRowContent(parser, row, skip);
8664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
8674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyStyle(parser, skip);
8684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
8704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
8724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_ROW.equals(tag)) {
8744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW));
8754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (!skip)
8764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            endRow(row);
8774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
8794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            || TAG_MERGE.equals(tag)) {
8804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
8814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
8834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        continue;
8844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
8864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
8884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKey(XmlPullParser parser, Row row, boolean skip)
8924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
8944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_KEY, parser);
8954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
8964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Key key = new Key(mResources, mParams, row, parser, mKeyStyles);
8974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d moreKeys=%s />",
8984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode,
8994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Arrays.toString(key.mMoreKeys)));
9004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_KEY, parser);
9014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                endKey(key);
9024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSpacer(XmlPullParser parser, Row row, boolean skip)
9064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
9084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_SPACER, parser);
9094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
9104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Key.Spacer spacer = new Key.Spacer(
9114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        mResources, mParams, row, parser, mKeyStyles);
9124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER));
9134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_SPACER, parser);
9144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                endKey(spacer);
9154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeKeyboardContent(XmlPullParser parser, boolean skip)
9194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseIncludeInternal(parser, null, skip);
9214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeRowContent(XmlPullParser parser, Row row, boolean skip)
9244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseIncludeInternal(parser, row, skip);
9264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip)
9294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
9314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
9324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
9334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
9344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Include);
9354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                int keyboardLayout = 0;
9364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
9374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    XmlParseUtils.checkAttributeExists(a,
9384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
9394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            TAG_INCLUDE, parser);
9404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    keyboardLayout = a.getResourceId(
9414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Include_keyboardLayout, 0);
9424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } finally {
9434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    a.recycle();
9444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
9474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />",
9484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout)));
9494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
9504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
9514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    parseMerge(parserForInclude, row, skip);
9524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } finally {
9534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    parserForInclude.close();
9544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseMerge(XmlPullParser parser, Row row, boolean skip)
9594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
9614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
9624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
9634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
9644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_MERGE.equals(tag)) {
9654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (row == null) {
9664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            parseKeyboardContent(parser, skip);
9674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        } else {
9684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            parseRowContent(parser, row, skip);
9694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        }
9704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
9714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
9724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.ParseException(
9734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "Included keyboard layout must have <merge> root element", parser);
9744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
9754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchKeyboardContent(XmlPullParser parser, boolean skip)
9804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseSwitchInternal(parser, null, skip);
9824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchRowContent(XmlPullParser parser, Row row, boolean skip)
9854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseSwitchInternal(parser, row, skip);
9874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip)
9904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId));
9924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            boolean selected = false;
9934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
9944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
9954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
9964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
9974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_CASE.equals(tag)) {
9984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        selected |= parseCase(parser, row, selected ? true : skip);
9994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_DEFAULT.equals(tag)) {
10004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        selected |= parseDefault(parser, row, selected ? true : skip);
10014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
10024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
10034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
10044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
10054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
10064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_SWITCH.equals(tag)) {
10074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_SWITCH));
10084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
10094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
10104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
10114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
10124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
10134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseCase(XmlPullParser parser, Row row, boolean skip)
10174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
10184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final boolean selected = parseCaseCondition(parser);
10194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (row == null) {
10204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Processing Rows.
10214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboardContent(parser, selected ? skip : true);
10224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
10234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Processing Keys.
10244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseRowContent(parser, row, selected ? skip : true);
10254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return selected;
10274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseCaseCondition(XmlPullParser parser) {
10304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final KeyboardId id = mParams.mId;
10314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (id == null)
10324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return true;
10334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
10354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Case);
10364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
10374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean modeMatched = matchTypedValue(a,
10384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
10394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean navigateActionMatched = matchBoolean(a,
10404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_navigateAction, id.navigateAction());
10414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean passwordInputMatched = matchBoolean(a,
10424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
10434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean hasSettingsKeyMatched = matchBoolean(a,
10444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_hasSettingsKey, id.hasSettingsKey());
10454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean f2KeyModeMatched = matchInteger(a,
10464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_f2KeyMode, id.f2KeyMode());
10474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean clobberSettingsKeyMatched = matchBoolean(a,
10484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
10494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean shortcutKeyEnabledMatched = matchBoolean(a,
10504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
10514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean hasShortcutKeyMatched = matchBoolean(a,
10524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
10534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // As noted at {@link KeyboardId} class, we are interested only in enum value
10544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // masked by {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and
10554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching
10564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // this attribute with id.mImeOptions as integer value is enough for our purpose.
10574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean imeActionMatched = matchInteger(a,
10584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_imeAction, id.imeAction());
10594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean localeCodeMatched = matchString(a,
10604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
10614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean languageCodeMatched = matchString(a,
10624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
10634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean countryCodeMatched = matchString(a,
10644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
10654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean selected = modeMatched && navigateActionMatched
10664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        && passwordInputMatched && hasSettingsKeyMatched && f2KeyModeMatched
10674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        && clobberSettingsKeyMatched && shortcutKeyEnabledMatched
10684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        && hasShortcutKeyMatched && imeActionMatched && localeCodeMatched
10694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        && languageCodeMatched && countryCodeMatched;
10704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE,
10724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
10734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_navigateAction, "navigateAction"),
10744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"),
10754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"),
10764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(KeyboardId.f2KeyModeName(
10774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"),
10784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
10794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "clobberSettingsKey"),
10804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
10814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "shortcutKeyEnabled"),
10824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"),
10834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(EditorInfoCompatUtils.imeOptionsName(
10844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"),
10854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"),
10864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
10874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "languageCode"),
10884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"),
10894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Boolean.toString(selected)));
10904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return selected;
10924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
10934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                a.recycle();
10944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchInteger(TypedArray a, int index, int value) {
10984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
10994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index) || a.getInt(index, 0) == value;
11014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchBoolean(TypedArray a, int index, boolean value) {
11044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index) || a.getBoolean(index, false) == value;
11074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchString(TypedArray a, int index, String value) {
11104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index)
11134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    || stringArrayContains(a.getString(index).split("\\|"), value);
11144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchTypedValue(TypedArray a, int index, int intValue,
11174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                String strValue) {
11184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue v = a.peekValue(index);
11214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (v == null)
11224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return true;
11234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isIntegerValue(v)) {
11254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return intValue == a.getInt(index, 0);
11264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else if (isStringValue(v)) {
11274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return stringArrayContains(a.getString(index).split("\\|"), strValue);
11284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return false;
11304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean stringArrayContains(String[] array, String value) {
11334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            for (final String elem : array) {
11344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (elem.equals(value))
11354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return true;
11364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return false;
11384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseDefault(XmlPullParser parser, Row row, boolean skip)
11414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
11424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
11434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (row == null) {
11444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboardContent(parser, skip);
11454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
11464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseRowContent(parser, row, skip);
11474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return true;
11494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyStyle(XmlPullParser parser, boolean skip)
11524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException {
11534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
11544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_KeyStyle);
11554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
11564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Key);
11574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
11584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
11594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
11604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            + "/> needs styleName attribute", parser);
11614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (!skip)
11624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
11634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
11644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyStyleAttr.recycle();
11654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttrs.recycle();
11664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void startKeyboard() {
11704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentY += mParams.mTopPadding;
11714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mTopEdge = true;
11724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void startRow(Row row) {
11754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
11764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentRow = row;
11774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mLeftEdge = true;
11784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = null;
11794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endRow(Row row) {
11824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mCurrentRow == null)
11834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new InflateException("orphant end row tag");
11844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mRightEdgeKey != null) {
11854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRightEdgeKey.markAsRightEdge(mParams);
11864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRightEdgeKey = null;
11874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
11894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentY += row.mRowHeight;
11904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentRow = null;
11914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mTopEdge = false;
11924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endKey(Key key) {
11954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.onAddKey(key);
11964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mLeftEdge) {
11974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                key.markAsLeftEdge(mParams);
11984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mLeftEdge = false;
11994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mTopEdge) {
12014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                key.markAsTopEdge(mParams);
12024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = key;
12044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endKeyboard() {
12074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // nothing to do here.
12084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void addEdgeSpace(float width, Row row) {
12114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            row.advanceXPos(width);
12124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mLeftEdge = false;
12134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = null;
12144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static float getDimensionOrFraction(TypedArray a, int index, int base,
12174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                float defValue) {
12184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue value = a.peekValue(index);
12194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (value == null)
12204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return defValue;
12214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isFractionValue(value)) {
12224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getFraction(index, base, base, defValue);
12234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else if (isDimensionValue(value)) {
12244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getDimension(index, defValue);
12254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return defValue;
12274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static int getEnumValue(TypedArray a, int index, int defValue) {
12304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue value = a.peekValue(index);
12314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (value == null)
12324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return defValue;
12334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isIntegerValue(value)) {
12344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getInt(index, defValue);
12354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return defValue;
12374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isFractionValue(TypedValue v) {
12404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_FRACTION;
12414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isDimensionValue(TypedValue v) {
12444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_DIMENSION;
12454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isIntegerValue(TypedValue v) {
12484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
12494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isStringValue(TypedValue v) {
12524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_STRING;
12534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static String textAttr(String value, String name) {
12564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return value != null ? String.format(" %s=%s", name, value) : "";
12574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static String booleanAttr(TypedArray a, int index, String name) {
12604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return a.hasValue(index)
12614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    ? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
12624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    }
12645a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka}
1265