Keyboard.java revision 42fcb2de641c4cd5d57f34889c8752401e35dcc8
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
70e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    /** Some common keys code.  These should be aligned with values/keycodes.xml */
71571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_ENTER = '\n';
72571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_TAB = '\t';
73571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_SPACE = ' ';
74571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_PERIOD = '.';
750730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    public static final int CODE_DASH = '-';
760730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    public static final int CODE_SINGLE_QUOTE = '\'';
770730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    public static final int CODE_DOUBLE_QUOTE = '"';
782b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    // TODO: Check how this should work for right-to-left languages. It seems to stand
792b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
802b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    // managed by the font? Or is it a different char?
812b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_PARENTHESIS = ')';
822b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
832b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_CURLY_BRACKET = '}';
842b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
85851c3267d4ab21f892b4164783bb4959c88b44ffTadashi G. Takaoka    public static final int CODE_DIGIT0 = '0';
86851c3267d4ab21f892b4164783bb4959c88b44ffTadashi G. Takaoka    public static final int CODE_PLUS = '+';
872b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard
88e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    /** Special keys code.  These should be aligned with values/keycodes.xml */
89c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka    public static final int CODE_DUMMY = 0;
90571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_SHIFT = -1;
91e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
92d2c5fdda862f6dd2a1e020cf674c35fbbc63fc92Tadashi G. Takaoka    public static final int CODE_CAPSLOCK = -3;
93571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_DELETE = -5;
94d2c5fdda862f6dd2a1e020cf674c35fbbc63fc92Tadashi G. Takaoka    public static final int CODE_SETTINGS = -6;
95cadb2128f54b49be31bb4dc06374afe81ed028b7Ken Wakasa    public static final int CODE_SHORTCUT = -7;
96c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka    // Code value representing the code is not specified.
97c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka    public static final int CODE_UNSPECIFIED = -99;
985a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
99167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka    public final KeyboardId mId;
10063584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka    public final int mThemeId;
101167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka
102167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka    /** Total height of the keyboard, including the padding and keys */
1038da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mOccupiedHeight;
1048da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    /** Total width of the keyboard, including the padding and keys */
1058da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mOccupiedWidth;
106167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka
1078fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    /** The padding above the keyboard */
1088fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    public final int mTopPadding;
1095a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    /** Default gap between rows */
1108da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mVerticalGap;
1118da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
1128fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    public final int mMostCommonKeyHeight;
1138da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mMostCommonKeyWidth;
1145a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1159d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    /** More keys keyboard template */
1169d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public final int mMoreKeysTemplate;
1179b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka
1189237a72634be821c22911633ef0848130e162d58Tadashi G. Takaoka    /** Maximum column for mini keyboard */
1199237a72634be821c22911633ef0848130e162d58Tadashi G. Takaoka    public final int mMaxMiniKeyboardColumn;
1209b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka
1217139335dcd84209285c028f5d83cb72bfbf1a966Tadashi G. Takaoka    /** True if Right-To-Left keyboard */
1228da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final boolean mIsRtlKeyboard;
1235a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1248da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    /** List of keys and icons in this keyboard */
1254a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    public final Set<Key> mKeys;
1264a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    public final Set<Key> mShiftKeys;
1278da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final Set<Key> mShiftLockKeys;
1288da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final KeyboardIconsSet mIconsSet;
1296fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka
1302013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka    private final Map<Integer, Key> mKeyCache = new HashMap<Integer, Key>();
131c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
1328fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    private final ProximityInfo mProximityInfo;
1338fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
134e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka    // TODO: Remove this variable.
135e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka    private final KeyboardShiftState mShiftState = new KeyboardShiftState();
136c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka
1374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    public Keyboard(Params params) {
1388da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mId = params.mId;
13963584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka        mThemeId = params.mThemeId;
1408da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mOccupiedHeight = params.mOccupiedHeight;
1418da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mOccupiedWidth = params.mOccupiedWidth;
1428fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka        mMostCommonKeyHeight = params.mMostCommonKeyHeight;
1438da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mMostCommonKeyWidth = params.mMostCommonKeyWidth;
1448da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mIsRtlKeyboard = params.mIsRtlKeyboard;
1459d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysTemplate = params.mMoreKeysTemplate;
1469237a72634be821c22911633ef0848130e162d58Tadashi G. Takaoka        mMaxMiniKeyboardColumn = params.mMaxMiniKeyboardColumn;
1478da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
1488fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka        mTopPadding = params.mTopPadding;
1498da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mVerticalGap = params.mVerticalGap;
1508da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
1514a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka        mKeys = Collections.unmodifiableSet(params.mKeys);
1524a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka        mShiftKeys = Collections.unmodifiableSet(params.mShiftKeys);
1538da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mShiftLockKeys = Collections.unmodifiableSet(params.mShiftLockKeys);
1548da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mIconsSet = params.mIconsSet;
1555a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1560d5494c66ac3e5947040e8148091163a1c8716f7satok        mProximityInfo = new ProximityInfo(
1578da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
158294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima                mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection);
1598fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1608fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
161043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard    public ProximityInfo getProximityInfo() {
162043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard        return mProximityInfo;
1635a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
1645a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1652013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka    public Key getKey(int code) {
166623d0155b6a316fdc9335370cdd4005bbb474ef3Tadashi G. Takaoka        if (code == CODE_DUMMY) {
167623d0155b6a316fdc9335370cdd4005bbb474ef3Tadashi G. Takaoka            return null;
168623d0155b6a316fdc9335370cdd4005bbb474ef3Tadashi G. Takaoka        }
1692013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        final Integer keyCode = code;
1702013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (mKeyCache.containsKey(keyCode)) {
1712013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            return mKeyCache.get(keyCode);
1722013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
1732013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
1742013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        for (final Key key : mKeys) {
1752013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            if (key.mCode == code) {
1762013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                mKeyCache.put(keyCode, key);
1772013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                return key;
1782013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
1792013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
1802013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        mKeyCache.put(keyCode, null);
1812013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        return null;
1822013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka    }
1832013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
184c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method.
1854a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    boolean hasShiftLockKey() {
1866d9bcd5e1317722207116ab6a3ddfcb152005701Tadashi G. Takaoka        return !mShiftLockKeys.isEmpty();
187571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
188571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
189c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method.
1904a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    void setShiftLocked(boolean newShiftLockState) {
1916d9bcd5e1317722207116ab6a3ddfcb152005701Tadashi G. Takaoka        for (final Key key : mShiftLockKeys) {
1926d9bcd5e1317722207116ab6a3ddfcb152005701Tadashi G. Takaoka            // To represent "shift locked" state. The highlight is handled by background image that
1936d9bcd5e1317722207116ab6a3ddfcb152005701Tadashi G. Takaoka            // might be a StateListDrawable.
194e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka            key.setHighlightOn(newShiftLockState);
19542fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka            final int attrId = newShiftLockState
19642fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka                    ? R.styleable.Keyboard_iconShiftKeyShifted
19742fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka                    : R.styleable.Keyboard_iconShiftKey;
19842fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka            key.setIcon(mIconsSet.getIconByAttrId(attrId));
199571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        }
200e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        mShiftState.setShiftLocked(newShiftLockState);
201571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
202571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
203c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Move this method to KeyboardId.
204571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public boolean isShiftLocked() {
205e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        return mShiftState.isShiftLocked();
206571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
207571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
208c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method.
2094a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    void setShifted(boolean newShiftState) {
210e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        if (!mShiftState.isShiftLocked()) {
2114f7f61730cbd45871c1e9044da84b494831f97c3Tadashi G. Takaoka            for (final Key key : mShiftKeys) {
21242fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka                final int attrId = newShiftState
21342fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka                        ? R.styleable.Keyboard_iconShiftKeyShifted
21442fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka                        : R.styleable.Keyboard_iconShiftKey;
21542fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka                key.setIcon(mIconsSet.getIconByAttrId(attrId));
216571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka            }
2175a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka        }
218e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        mShiftState.setShifted(newShiftState);
2195a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
2205a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
221c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Move this method to KeyboardId.
2225a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    public boolean isShiftedOrShiftLocked() {
223e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        return mShiftState.isShiftedOrShiftLocked();
2245a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
2255a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
226c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method
2274a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    void setAutomaticTemporaryUpperCase() {
228e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        mShiftState.setAutomaticTemporaryUpperCase();
229571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
230571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
231c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Move this method to KeyboardId.
232571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public boolean isManualTemporaryUpperCase() {
233e466583ddc68278ad708094f8ac521be504bf342Tadashi G. Takaoka        return mShiftState.isManualTemporaryUpperCase();
234571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
235571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
236c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method.
237d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka    public CharSequence adjustLabelCase(CharSequence label) {
2381ebedd7a15a8fd94e68fb43eb089ed87c4c5a480Tadashi G. Takaoka        if (mId.isAlphabetKeyboard() && isShiftedOrShiftLocked() && !TextUtils.isEmpty(label)
2391ebedd7a15a8fd94e68fb43eb089ed87c4c5a480Tadashi G. Takaoka                && label.length() < 3 && Character.isLowerCase(label.charAt(0))) {
240d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka            return label.toString().toUpperCase(mId.mLocale);
241d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka        }
242d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka        return label;
243571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
244571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
2454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    public static class Params {
2464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public KeyboardId mId;
2474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mThemeId;
2484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        /** Total height and width of the keyboard, including the paddings and keys */
2504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mOccupiedHeight;
2514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mOccupiedWidth;
2524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        /** Base height and width of the keyboard used to calculate rows' or keys' heights and
2544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         *  widths
2554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         */
2564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mBaseHeight;
2574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mBaseWidth;
2584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mTopPadding;
2604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mBottomPadding;
2614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mHorizontalEdgesPadding;
2624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mHorizontalCenterPadding;
2634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mDefaultRowHeight;
2654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mDefaultKeyWidth;
2664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mHorizontalGap;
2674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mVerticalGap;
2684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public boolean mIsRtlKeyboard;
2704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMoreKeysTemplate;
2714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMaxMiniKeyboardColumn;
2724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int GRID_WIDTH;
2744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int GRID_HEIGHT;
2754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final Set<Key> mKeys = new HashSet<Key>();
2774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final Set<Key> mShiftKeys = new HashSet<Key>();
2784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final Set<Key> mShiftLockKeys = new HashSet<Key>();
2794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
2804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMostCommonKeyHeight = 0;
2824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMostCommonKeyWidth = 0;
2834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final TouchPositionCorrection mTouchPositionCorrection =
2854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                new TouchPositionCorrection();
2864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static class TouchPositionCorrection {
2884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3;
2894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public boolean mEnabled;
2914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float[] mXs;
2924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float[] mYs;
2934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float[] mRadii;
2944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void load(String[] data) {
2964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int dataLength = data.length;
2974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
2984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (LatinImeLogger.sDBG)
2994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new RuntimeException(
3004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "the size of touch position correction data is invalid");
3014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return;
3024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
3034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
3054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mXs = new float[length];
3064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mYs = new float[length];
3074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRadii = new float[length];
3084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
3094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    for (int i = 0; i < dataLength; ++i) {
3104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE;
3114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
3124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        final float value = Float.parseFloat(data[i]);
3134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (type == 0) {
3144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mXs[index] = value;
3154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        } else if (type == 1) {
3164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mYs[index] = value;
3174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        } else {
3184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mRadii[index] = value;
3194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        }
3204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
3214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } catch (NumberFormatException e) {
3224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (LatinImeLogger.sDBG) {
3234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new RuntimeException(
3244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "the number format for touch position correction data is invalid");
3254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
3264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mXs = null;
3274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mYs = null;
3284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mRadii = null;
3294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
3304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void setEnabled(boolean enabled) {
3334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mEnabled = enabled;
3344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public boolean isValid() {
3374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mEnabled && mXs != null && mYs != null && mRadii != null
3384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
3394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected void clearKeys() {
3434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mKeys.clear();
3444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mShiftKeys.clear();
3454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mShiftLockKeys.clear();
3464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            clearHistogram();
3474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public void onAddKey(Key key) {
3504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mKeys.add(key);
3514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            updateHistogram(key);
3524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (key.mCode == Keyboard.CODE_SHIFT) {
3534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mShiftKeys.add(key);
3544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (key.isSticky()) {
3554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mShiftLockKeys.add(key);
3564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
3574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private int mMaxHeightCount = 0;
3614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private int mMaxWidthCount = 0;
3624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final Map<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>();
3634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final Map<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>();
3644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void clearHistogram() {
3664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMostCommonKeyHeight = 0;
3674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMaxHeightCount = 0;
3684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mHeightHistogram.clear();
3694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMaxWidthCount = 0;
3714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMostCommonKeyWidth = 0;
3724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mWidthHistogram.clear();
3734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static int updateHistogramCounter(Map<Integer, Integer> histogram, Integer key) {
3764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1;
3774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            histogram.put(key, count);
3784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return count;
3794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void updateHistogram(Key key) {
3824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final Integer height = key.mHeight + key.mVerticalGap;
3834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int heightCount = updateHistogramCounter(mHeightHistogram, height);
3844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (heightCount > mMaxHeightCount) {
3854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMaxHeightCount = heightCount;
3864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMostCommonKeyHeight = height;
3874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final Integer width = key.mWidth + key.mHorizontalGap;
3904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int widthCount = updateHistogramCounter(mWidthHistogram, width);
3914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (widthCount > mMaxWidthCount) {
3924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMaxWidthCount = widthCount;
3934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMostCommonKeyWidth = width;
3944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    }
3974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3985a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    /**
3990c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka     * Returns the array of the keys that are closest to the given point.
4005a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * @param x the x-coordinate of the point
4015a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * @param y the y-coordinate of the point
4020c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka     * @return the array of the nearest keys to the given point. If the given
4035a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * point is out of range, then an array of size zero is returned.
4045a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     */
4050c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka    public Key[] getNearestKeys(int x, int y) {
4060d5494c66ac3e5947040e8148091163a1c8716f7satok        return mProximityInfo.getNearestKeys(x, y);
4075a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
40863584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka
4097dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka    public static String printableCode(int code) {
4107dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        switch (code) {
4117dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_SHIFT: return "shift";
4127dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_SWITCH_ALPHA_SYMBOL: return "symbol";
4137dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_CAPSLOCK: return "capslock";
4147dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_DELETE: return "delete";
4157dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_SHORTCUT: return "shortcut";
4167dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_DUMMY: return "dummy";
4177dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_UNSPECIFIED: return "unspec";
4187dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        default:
4197dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka            if (code < 0) Log.w(TAG, "Unknow negative key code=" + code);
4207dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka            if (code < 0x100) return String.format("\\u%02x", code);
4217dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka            return String.format("\\u04x", code);
4227dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        }
4237dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka    }
4244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    /**
4264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * Keyboard Building helper.
4274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *
4284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * This class parses Keyboard XML file and eventually build a Keyboard.
4294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * The Keyboard XML file looks like:
4304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/keyboard.xml --&lt;
4324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;Keyboard keyboard_attributes*&lt;
4334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;!-- Keyboard Content --&lt;
4344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Row row_attributes*&lt;
4354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Row Content --&lt;
4364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Key key_attributes* /&lt;
4374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Spacer horizontalGap="0.2in" /&lt;
4384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;include keyboardLayout="@xml/other_keys"&lt;
4394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       ...
4404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/Row&lt;
4414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;include keyboardLayout="@xml/other_rows"&lt;
4424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/Keyboard&lt;
4444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * The XML file which is included in other file must have &gt;merge&lt; as root element,
4464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * such as:
4474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/other_keys.xml --&lt;
4494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;merge&lt;
4504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Key key_attributes* /&lt;
4514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/merge&lt;
4534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * and
4554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/other_rows.xml --&lt;
4574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;merge&lt;
4584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Row row_attributes*&lt;
4594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Key key_attributes* /&lt;
4604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/Row&lt;
4614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/merge&lt;
4634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * You can also use switch-case-default tags to select Rows and Keys.
4654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;switch&lt;
4674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;case case_attribute*&lt;
4684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Any valid tags at switch position --&lt;
4694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/case&lt;
4704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;default&lt;
4724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Any valid tags at switch position --&lt;
4734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/default&lt;
4744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/switch&lt;
4754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * You can declare Key style and specify styles within Key tags.
4774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;switch&lt;
4794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;case mode="email"&lt;
4804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
4814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *           keyLabel=".com"
4824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         /&lt;
4834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;/case&lt;
4844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;case mode="url"&lt;
4854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
4864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *           keyLabel="http://"
4874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         /&lt;
4884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;/case&lt;
4894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/switch&lt;
4904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Key keyStyle="shift-key" ... /&lt;
4924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     */
4944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    public static class Builder<KP extends Params> {
4964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG = Builder.class.getSimpleName();
4974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final boolean DEBUG = false;
4984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        // Keyboard XML Tags
5004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_KEYBOARD = "Keyboard";
5014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_ROW = "Row";
5024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_KEY = "Key";
5034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_SPACER = "Spacer";
5044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_INCLUDE = "include";
5054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_MERGE = "merge";
5064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_SWITCH = "switch";
5074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_CASE = "case";
5084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_DEFAULT = "default";
5094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static final String TAG_KEY_STYLE = "key-style";
5104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
5124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final int DEFAULT_KEYBOARD_ROWS = 4;
5134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final KP mParams;
5154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final Context mContext;
5164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final Resources mResources;
5174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final DisplayMetrics mDisplayMetrics;
5184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private int mCurrentY = 0;
5204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Row mCurrentRow = null;
5214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean mLeftEdge;
5224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean mTopEdge;
5234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Key mRightEdgeKey = null;
5244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final KeyStyles mKeyStyles = new KeyStyles();
5254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        /**
5274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
5284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
5294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * defines.
5304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         */
5314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static class Row {
5324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // keyWidth enum constants
5334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_NOT_ENUM = 0;
5344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_FILL_RIGHT = -1;
5354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_FILL_BOTH = -2;
5364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private final Params mParams;
5384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            /** Default width of a key in this row. */
5394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public final float mDefaultKeyWidth;
5404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            /** Default height of a key in this row. */
5414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public final int mRowHeight;
5424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private final int mCurrentY;
5444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // Will be updated by {@link Key}'s constructor.
5454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private float mCurrentX;
5464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public Row(Resources res, Params params, XmlPullParser parser, int y) {
5484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mParams = params;
5494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
5504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard);
5514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRowHeight = (int)Builder.getDimensionOrFraction(keyboardAttr,
5524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_rowHeight,
5534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseHeight, params.mDefaultRowHeight);
5544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyboardAttr.recycle();
5554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
5564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key);
5574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mDefaultKeyWidth = Builder.getDimensionOrFraction(keyAttr,
5584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth,
5594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseWidth, params.mDefaultKeyWidth);
5604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttr.recycle();
5614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentY = y;
5634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX = 0.0f;
5644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void setXPos(float keyXPos) {
5674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX = keyXPos;
5684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void advanceXPos(float width) {
5714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX += width;
5724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public int getKeyY() {
5754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mCurrentY;
5764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float getKeyX(TypedArray keyAttr) {
5794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int widthType = Builder.getEnumValue(keyAttr,
5804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
5814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (widthType == KEYWIDTH_FILL_BOTH) {
5824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillBoth, the key width should start right after the nearest
5834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // key on the left hand side.
5844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return mCurrentX;
5854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
5864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int keyboardRightEdge = mParams.mOccupiedWidth
5884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - mParams.mHorizontalEdgesPadding;
5894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
5904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final float keyXPos = Builder.getDimensionOrFraction(keyAttr,
5914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0);
5924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (keyXPos < 0) {
5934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // If keyXPos is negative, the actual x-coordinate will be
5944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // keyboardWidth + keyXPos.
5954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // keyXPos shouldn't be less than mCurrentX because drawable area for this
5964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // key starts at mCurrentX. Or, this key will overlaps the adjacent key on
5974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // its left hand side.
5984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
5994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
6004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        return keyXPos + mParams.mHorizontalEdgesPadding;
6014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
6024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
6034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mCurrentX;
6044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float getKeyWidth(TypedArray keyAttr, float keyXPos) {
6074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int widthType = Builder.getEnumValue(keyAttr,
6084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
6094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                switch (widthType) {
6104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                case KEYWIDTH_FILL_RIGHT:
6114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                case KEYWIDTH_FILL_BOTH:
6124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final int keyboardRightEdge =
6134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
6144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillRight, the actual key width will be determined to fill
6154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // out the area up to the right edge of the keyboard.
6164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillBoth, the actual key width will be determined to fill out
6174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // the area between the nearest key on the left hand side and the right edge of
6184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // the keyboard.
6194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return keyboardRightEdge - keyXPos;
6204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                default: // KEYWIDTH_NOT_ENUM
6214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return Builder.getDimensionOrFraction(keyAttr,
6224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Key_keyWidth,
6234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mParams.mBaseWidth, mDefaultKeyWidth);
6244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
6254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Builder(Context context, KP params) {
6294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mContext = context;
6304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final Resources res = context.getResources();
6314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mResources = res;
6324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mDisplayMetrics = res.getDisplayMetrics();
6334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams = params;
6354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            setTouchPositionCorrectionData(context, params);
6374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
6394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
6404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static void setTouchPositionCorrectionData(Context context, Params params) {
6434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = context.obtainStyledAttributes(
6444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    null, R.styleable.Keyboard, R.attr.keyboardStyle, 0);
6454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.mThemeId = a.getInt(R.styleable.Keyboard_themeId, 0);
6464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int resourceId = a.getResourceId(
6474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_touchPositionCorrectionData, 0);
6484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            a.recycle();
6494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (resourceId == 0) {
6504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (LatinImeLogger.sDBG)
6514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new RuntimeException("touchPositionCorrectionData is not defined");
6524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return;
6534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final String[] data = context.getResources().getStringArray(resourceId);
6564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.mTouchPositionCorrection.load(data);
6574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Builder<KP> load(int xmlId, KeyboardId id) {
6604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.mId = id;
6614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final XmlResourceParser parser = mResources.getXml(xmlId);
6624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
6634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboard(parser);
6644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } catch (XmlPullParserException e) {
6654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                Log.w(TAG, "keyboard XML parse error: " + e);
6664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new IllegalArgumentException(e);
6674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } catch (IOException e) {
6684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                Log.w(TAG, "keyboard XML parse error: " + e);
6694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new RuntimeException(e);
6704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
6714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parser.close();
6724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return this;
6744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public void setTouchPositionCorrectionEnabled(boolean enabled) {
6774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.mTouchPositionCorrection.setEnabled(enabled);
6784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Keyboard build() {
6814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return new Keyboard(mParams);
6824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboard(XmlPullParser parser)
6854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
6864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId));
6874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
6884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
6894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
6904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
6914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEYBOARD.equals(tag)) {
6924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyboardAttributes(parser);
6934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        startKeyboard();
6944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyboardContent(parser, false);
6954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
6964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
6974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD);
6984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
6994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
7004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
7014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboardAttributes(XmlPullParser parser) {
7044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int displayWidth = mDisplayMetrics.widthPixels;
7054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
7064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
7074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.style.Keyboard);
7084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
7094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Key);
7104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
7114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int displayHeight = mDisplayMetrics.heightPixels;
7124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int keyboardHeight = (int)keyboardAttr.getDimension(
7134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
7144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int maxKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
7154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
7164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                int minKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
7174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2);
7184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (minKeyboardHeight < 0) {
7194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // Specified fraction was negative, so it should be calculated against display
7204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // width.
7214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    minKeyboardHeight = -(int)getDimensionOrFraction(keyboardAttr,
7224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
7234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
7244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Params params = mParams;
7254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Keyboard height will not exceed maxKeyboardHeight and will not be less than
7264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // minKeyboardHeight.
7274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mOccupiedHeight = Math.max(
7284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
7294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mOccupiedWidth = params.mId.mWidth;
7304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mTopPadding = (int)getDimensionOrFraction(keyboardAttr,
7314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0);
7324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr,
7334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0);
7344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mHorizontalEdgesPadding = (int)getDimensionOrFraction(keyboardAttr,
7354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardHorizontalEdgesPadding,
7364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        mParams.mOccupiedWidth, 0);
7374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2
7394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - params.mHorizontalCenterPadding;
7404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mDefaultKeyWidth = (int)getDimensionOrFraction(keyAttr,
7414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth,
7424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS);
7434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr,
7444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0);
7454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr,
7464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0);
7474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding
7484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - params.mBottomPadding + params.mVerticalGap;
7494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr,
7504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_rowHeight, params.mBaseHeight,
7514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
7524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mIsRtlKeyboard = keyboardAttr.getBoolean(
7544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_isRtlKeyboard, false);
7554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mMoreKeysTemplate = keyboardAttr.getResourceId(
7564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_moreKeysTemplate, 0);
7574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mMaxMiniKeyboardColumn = keyAttr.getInt(
7584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
7594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mIconsSet.loadIcons(keyboardAttr);
7614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
7624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttr.recycle();
7634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyboardAttr.recycle();
7644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
7654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboardContent(XmlPullParser parser, boolean skip)
7684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
7694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
7704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
7714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
7724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
7734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_ROW.equals(tag)) {
7744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Row row = parseRowAttributes(parser);
7754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW));
7764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (!skip)
7774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            startRow(row);
7784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseRowContent(parser, row, skip);
7794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_INCLUDE.equals(tag)) {
7804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseIncludeKeyboardContent(parser, skip);
7814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SWITCH.equals(tag)) {
7824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSwitchKeyboardContent(parser, skip);
7834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
7844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyStyle(parser, skip);
7854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
7864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW);
7874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
7884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
7894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
7904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEYBOARD.equals(tag)) {
7914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        endKeyboard();
7924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
7934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
7944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            || TAG_MERGE.equals(tag)) {
7954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
7964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
7974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
7984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        continue;
7994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
8014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
8034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Row parseRowAttributes(XmlPullParser parser) throws XmlPullParserException {
8074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
8084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard);
8094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
8104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (a.hasValue(R.styleable.Keyboard_horizontalGap))
8114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
8124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (a.hasValue(R.styleable.Keyboard_verticalGap))
8134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
8144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return new Row(mResources, mParams, parser, mCurrentY);
8154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
8164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                a.recycle();
8174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseRowContent(XmlPullParser parser, Row row, boolean skip)
8214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
8234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
8244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
8254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEY.equals(tag)) {
8274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKey(parser, row, skip);
8284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SPACER.equals(tag)) {
8294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSpacer(parser, row, skip);
8304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_INCLUDE.equals(tag)) {
8314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseIncludeRowContent(parser, row, skip);
8324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SWITCH.equals(tag)) {
8334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSwitchRowContent(parser, row, skip);
8344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
8354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyStyle(parser, skip);
8364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
8384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
8404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_ROW.equals(tag)) {
8424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW));
8434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (!skip)
8444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            endRow(row);
8454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
8474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            || TAG_MERGE.equals(tag)) {
8484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
8494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
8514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        continue;
8524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
8544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
8564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKey(XmlPullParser parser, Row row, boolean skip)
8604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
8624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_KEY, parser);
8634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
8644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Key key = new Key(mResources, mParams, row, parser, mKeyStyles);
8654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d moreKeys=%s />",
8664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode,
8674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Arrays.toString(key.mMoreKeys)));
8684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_KEY, parser);
8694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                endKey(key);
8704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSpacer(XmlPullParser parser, Row row, boolean skip)
8744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
8764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_SPACER, parser);
8774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
8784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Key.Spacer spacer = new Key.Spacer(
8794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        mResources, mParams, row, parser, mKeyStyles);
8804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER));
8814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_SPACER, parser);
8824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                endKey(spacer);
8834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeKeyboardContent(XmlPullParser parser, boolean skip)
8874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseIncludeInternal(parser, null, skip);
8894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeRowContent(XmlPullParser parser, Row row, boolean skip)
8924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseIncludeInternal(parser, row, skip);
8944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip)
8974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
8994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
9004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
9014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
9024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Include);
9034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                int keyboardLayout = 0;
9044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
9054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    XmlParseUtils.checkAttributeExists(a,
9064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
9074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            TAG_INCLUDE, parser);
9084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    keyboardLayout = a.getResourceId(
9094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Include_keyboardLayout, 0);
9104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } finally {
9114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    a.recycle();
9124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
9154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />",
9164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout)));
9174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
9184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
9194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    parseMerge(parserForInclude, row, skip);
9204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } finally {
9214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    parserForInclude.close();
9224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseMerge(XmlPullParser parser, Row row, boolean skip)
9274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
9294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
9304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
9314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
9324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_MERGE.equals(tag)) {
9334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (row == null) {
9344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            parseKeyboardContent(parser, skip);
9354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        } else {
9364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            parseRowContent(parser, row, skip);
9374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        }
9384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
9394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
9404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.ParseException(
9414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "Included keyboard layout must have <merge> root element", parser);
9424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
9434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchKeyboardContent(XmlPullParser parser, boolean skip)
9484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseSwitchInternal(parser, null, skip);
9504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchRowContent(XmlPullParser parser, Row row, boolean skip)
9534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseSwitchInternal(parser, row, skip);
9554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip)
9584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId));
9604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            boolean selected = false;
9614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
9624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
9634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
9644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
9654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_CASE.equals(tag)) {
9664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        selected |= parseCase(parser, row, selected ? true : skip);
9674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_DEFAULT.equals(tag)) {
9684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        selected |= parseDefault(parser, row, selected ? true : skip);
9694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
9704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
9714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
9724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
9734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
9744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_SWITCH.equals(tag)) {
9754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_SWITCH));
9764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
9774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
9784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
9794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
9804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseCase(XmlPullParser parser, Row row, boolean skip)
9854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final boolean selected = parseCaseCondition(parser);
9874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (row == null) {
9884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Processing Rows.
9894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboardContent(parser, selected ? skip : true);
9904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
9914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Processing Keys.
9924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseRowContent(parser, row, selected ? skip : true);
9934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return selected;
9954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseCaseCondition(XmlPullParser parser) {
9984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final KeyboardId id = mParams.mId;
9994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (id == null)
10004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return true;
10014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
10034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Case);
10044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
10054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean modeMatched = matchTypedValue(a,
10064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
10074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean navigateActionMatched = matchBoolean(a,
10084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_navigateAction, id.navigateAction());
10094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean passwordInputMatched = matchBoolean(a,
10104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
10114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean hasSettingsKeyMatched = matchBoolean(a,
10124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_hasSettingsKey, id.hasSettingsKey());
10134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean f2KeyModeMatched = matchInteger(a,
10144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_f2KeyMode, id.f2KeyMode());
10154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean clobberSettingsKeyMatched = matchBoolean(a,
10164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
10174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean shortcutKeyEnabledMatched = matchBoolean(a,
10184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
10194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean hasShortcutKeyMatched = matchBoolean(a,
10204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
10214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // As noted at {@link KeyboardId} class, we are interested only in enum value
10224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // masked by {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and
10234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching
10244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // this attribute with id.mImeOptions as integer value is enough for our purpose.
10254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean imeActionMatched = matchInteger(a,
10264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_imeAction, id.imeAction());
10274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean localeCodeMatched = matchString(a,
10284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
10294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean languageCodeMatched = matchString(a,
10304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
10314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean countryCodeMatched = matchString(a,
10324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
10334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean selected = modeMatched && navigateActionMatched
10344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        && passwordInputMatched && hasSettingsKeyMatched && f2KeyModeMatched
10354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        && clobberSettingsKeyMatched && shortcutKeyEnabledMatched
10364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        && hasShortcutKeyMatched && imeActionMatched && localeCodeMatched
10374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        && languageCodeMatched && countryCodeMatched;
10384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi 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,
10404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
10414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_navigateAction, "navigateAction"),
10424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"),
10434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"),
10444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(KeyboardId.f2KeyModeName(
10454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"),
10464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
10474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "clobberSettingsKey"),
10484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
10494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "shortcutKeyEnabled"),
10504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"),
10514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(EditorInfoCompatUtils.imeOptionsName(
10524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"),
10534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"),
10544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
10554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "languageCode"),
10564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"),
10574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Boolean.toString(selected)));
10584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return selected;
10604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
10614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                a.recycle();
10624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchInteger(TypedArray a, int index, int value) {
10664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
10674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
10684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index) || a.getInt(index, 0) == value;
10694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchBoolean(TypedArray a, int index, boolean value) {
10724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
10734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
10744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index) || a.getBoolean(index, false) == value;
10754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchString(TypedArray a, int index, String value) {
10784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
10794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
10804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index)
10814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    || stringArrayContains(a.getString(index).split("\\|"), value);
10824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchTypedValue(TypedArray a, int index, int intValue,
10854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                String strValue) {
10864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
10874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
10884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue v = a.peekValue(index);
10894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (v == null)
10904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return true;
10914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isIntegerValue(v)) {
10934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return intValue == a.getInt(index, 0);
10944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else if (isStringValue(v)) {
10954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return stringArrayContains(a.getString(index).split("\\|"), strValue);
10964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return false;
10984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean stringArrayContains(String[] array, String value) {
11014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            for (final String elem : array) {
11024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (elem.equals(value))
11034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return true;
11044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return false;
11064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseDefault(XmlPullParser parser, Row row, boolean skip)
11094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
11104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
11114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (row == null) {
11124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboardContent(parser, skip);
11134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
11144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseRowContent(parser, row, skip);
11154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return true;
11174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyStyle(XmlPullParser parser, boolean skip)
11204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException {
11214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
11224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_KeyStyle);
11234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
11244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Key);
11254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
11264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
11274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
11284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            + "/> needs styleName attribute", parser);
11294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (!skip)
11304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
11314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
11324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyStyleAttr.recycle();
11334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttrs.recycle();
11344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void startKeyboard() {
11384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentY += mParams.mTopPadding;
11394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mTopEdge = true;
11404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void startRow(Row row) {
11434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
11444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentRow = row;
11454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mLeftEdge = true;
11464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = null;
11474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endRow(Row row) {
11504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mCurrentRow == null)
11514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new InflateException("orphant end row tag");
11524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mRightEdgeKey != null) {
11534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRightEdgeKey.markAsRightEdge(mParams);
11544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRightEdgeKey = null;
11554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
11574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentY += row.mRowHeight;
11584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentRow = null;
11594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mTopEdge = false;
11604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endKey(Key key) {
11634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.onAddKey(key);
11644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mLeftEdge) {
11654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                key.markAsLeftEdge(mParams);
11664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mLeftEdge = false;
11674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mTopEdge) {
11694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                key.markAsTopEdge(mParams);
11704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = key;
11724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endKeyboard() {
11754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // nothing to do here.
11764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void addEdgeSpace(float width, Row row) {
11794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            row.advanceXPos(width);
11804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mLeftEdge = false;
11814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = null;
11824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static float getDimensionOrFraction(TypedArray a, int index, int base,
11854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                float defValue) {
11864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue value = a.peekValue(index);
11874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (value == null)
11884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return defValue;
11894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isFractionValue(value)) {
11904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getFraction(index, base, base, defValue);
11914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else if (isDimensionValue(value)) {
11924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getDimension(index, defValue);
11934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return defValue;
11954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static int getEnumValue(TypedArray a, int index, int defValue) {
11984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue value = a.peekValue(index);
11994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (value == null)
12004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return defValue;
12014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isIntegerValue(value)) {
12024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getInt(index, defValue);
12034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return defValue;
12054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isFractionValue(TypedValue v) {
12084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_FRACTION;
12094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isDimensionValue(TypedValue v) {
12124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_DIMENSION;
12134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isIntegerValue(TypedValue v) {
12164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
12174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isStringValue(TypedValue v) {
12204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_STRING;
12214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static String textAttr(String value, String name) {
12244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return value != null ? String.format(" %s=%s", name, value) : "";
12254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static String booleanAttr(TypedArray a, int index, String name) {
12284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return a.hasValue(index)
12294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    ? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
12304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    }
12325a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka}
1233