Keyboard.java revision 7ef1dabd92a9dae042965cd10d08a2cd47455dcc
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;
23e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatokimport android.text.TextUtils;
24a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaokaimport android.util.AttributeSet;
254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.util.DisplayMetrics;
267dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaokaimport android.util.Log;
274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.util.TypedValue;
284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.util.Xml;
294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport android.view.InflateException;
305a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyStyles;
32c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport com.android.inputmethod.latin.XmlParseUtils;
36b7758d6f912093747d4b18fbc8d1dcd77c7d1f9bTadashi G. Takaoka
374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport org.xmlpull.v1.XmlPullParser;
384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport org.xmlpull.v1.XmlPullParserException;
394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport java.io.IOException;
41e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatokimport java.util.ArrayList;
424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport java.util.Arrays;
438da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaokaimport java.util.Collections;
442013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaokaimport java.util.HashMap;
454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaokaimport java.util.HashSet;
46e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatokimport java.util.List;
47167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaokaimport java.util.Map;
48167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaokaimport java.util.Set;
495a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
505a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka/**
515a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
525a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * consists of rows of keys.
535a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
545a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * <pre>
555a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * &lt;Keyboard
565a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         latin:keyWidth="%10p"
575a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         latin:keyHeight="50px"
585a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         latin:horizontalGap="2px"
595a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         latin:verticalGap="2px" &gt;
605a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *     &lt;Row latin:keyWidth="32px" &gt;
615a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         &lt;Key latin:keyLabel="A" /&gt;
625a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         ...
635a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *     &lt;/Row&gt;
645a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *     ...
655a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * &lt;/Keyboard&gt;
665a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * </pre>
675a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka */
685a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapublic class Keyboard {
697dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka    private static final String TAG = Keyboard.class.getSimpleName();
707dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka
71ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka    /** Some common keys code. Must be positive.
72ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka     * These should be aligned with values/keycodes.xml
73ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka     */
74571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_ENTER = '\n';
75571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_TAB = '\t';
76571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_SPACE = ' ';
77571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_PERIOD = '.';
780730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    public static final int CODE_DASH = '-';
790730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    public static final int CODE_SINGLE_QUOTE = '\'';
800730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard    public static final int CODE_DOUBLE_QUOTE = '"';
812b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    // TODO: Check how this should work for right-to-left languages. It seems to stand
822b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
832b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    // managed by the font? Or is it a different char?
842b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_PARENTHESIS = ')';
852b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
862b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_CURLY_BRACKET = '}';
872b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard    public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
888cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka    private static final int MINIMUM_LETTER_CODE = CODE_TAB;
892b4eabed2bfe982b91a994c145401d98894e6ef5Jean Chalard
908cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka    /** Special keys code. Must be negative.
91ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka     * These should be aligned with values/keycodes.xml
92ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka     */
93571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    public static final int CODE_SHIFT = -1;
94e18bd3e323e7d7448677bb66e8149eea0169c771Tadashi G. Takaoka    public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
957a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    public static final int CODE_OUTPUT_TEXT = -3;
967a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    public static final int CODE_DELETE = -4;
977a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    public static final int CODE_SETTINGS = -5;
987a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    public static final int CODE_SHORTCUT = -6;
997a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka    public static final int CODE_ACTION_ENTER = -7;
10005bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka    public static final int CODE_ACTION_NEXT = -8;
10105bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka    public static final int CODE_ACTION_PREVIOUS = -9;
10281d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    public static final int CODE_LANGUAGE_SWITCH = -10;
103c4f71668d7b8203dc66f0f04c089a363189eb4ceTadashi G. Takaoka    // Code value representing the code is not specified.
10481d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka    public static final int CODE_UNSPECIFIED = -11;
1055a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
106167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka    public final KeyboardId mId;
10763584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka    public final int mThemeId;
108167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka
109167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka    /** Total height of the keyboard, including the padding and keys */
1108da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mOccupiedHeight;
1118da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    /** Total width of the keyboard, including the padding and keys */
1128da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mOccupiedWidth;
113167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka
1148fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    /** The padding above the keyboard */
1158fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    public final int mTopPadding;
1165a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    /** Default gap between rows */
1178da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mVerticalGap;
1188da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
1198fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    public final int mMostCommonKeyHeight;
1208da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mMostCommonKeyWidth;
1215a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1229d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    /** More keys keyboard template */
1239d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public final int mMoreKeysTemplate;
1249b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka
1252affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    /** Maximum column for more keys keyboard */
1262affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    public final int mMaxMoreKeysKeyboardColumn;
1279b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka
1288da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    /** List of keys and icons in this keyboard */
1294a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    public final Set<Key> mKeys;
1304a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka    public final Set<Key> mShiftKeys;
1318da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final KeyboardIconsSet mIconsSet;
1326fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka
1332013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka    private final Map<Integer, Key> mKeyCache = new HashMap<Integer, Key>();
134c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
1358fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    private final ProximityInfo mProximityInfo;
1368fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
137e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok    public final Map<Integer, List<Integer>> mAdditionalProximityChars;
138e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok
1394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    public Keyboard(Params params) {
1408da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mId = params.mId;
14163584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka        mThemeId = params.mThemeId;
1428da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mOccupiedHeight = params.mOccupiedHeight;
1438da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mOccupiedWidth = params.mOccupiedWidth;
1448fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka        mMostCommonKeyHeight = params.mMostCommonKeyHeight;
1458da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mMostCommonKeyWidth = params.mMostCommonKeyWidth;
1469d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysTemplate = params.mMoreKeysTemplate;
1472affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn;
1488da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
1498fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka        mTopPadding = params.mTopPadding;
1508da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mVerticalGap = params.mVerticalGap;
1518da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
1524a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka        mKeys = Collections.unmodifiableSet(params.mKeys);
1534a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka        mShiftKeys = Collections.unmodifiableSet(params.mShiftKeys);
1548da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mIconsSet = params.mIconsSet;
155e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok        mAdditionalProximityChars = params.mAdditionalProximityChars;
1565a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1577ef1dabd92a9dae042965cd10d08a2cd47455dccsatok        mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(),
1588da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
159e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection,
160e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                params.mAdditionalProximityChars);
1618fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1628fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
163043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard    public ProximityInfo getProximityInfo() {
164043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard        return mProximityInfo;
1655a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
1665a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1672013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka    public Key getKey(int code) {
1688cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka        if (code == CODE_UNSPECIFIED) {
169623d0155b6a316fdc9335370cdd4005bbb474ef3Tadashi G. Takaoka            return null;
170623d0155b6a316fdc9335370cdd4005bbb474ef3Tadashi G. Takaoka        }
1712013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        final Integer keyCode = code;
1722013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        if (mKeyCache.containsKey(keyCode)) {
1732013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            return mKeyCache.get(keyCode);
1742013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
1752013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
1762013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        for (final Key key : mKeys) {
1772013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            if (key.mCode == code) {
1782013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                mKeyCache.put(keyCode, key);
1792013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka                return key;
1802013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
1812013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
1822013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        mKeyCache.put(keyCode, null);
1832013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        return null;
1842013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka    }
1852013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
186c1859b8fd22b2c4cc2a700566f57f3e3d04e1580Tadashi G. Takaoka    // TODO: Remove this method.
1875a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    public boolean isShiftedOrShiftLocked() {
188ede2133cb137a48aabd2aefc464f68edb7fd2514Tadashi G. Takaoka        // Alphabet mode have unshifted, manual shifted, automatic shifted, shift locked, and
189ede2133cb137a48aabd2aefc464f68edb7fd2514Tadashi G. Takaoka        // shift lock shifted element. So that unshifed element is the only one that is NOT in
190ede2133cb137a48aabd2aefc464f68edb7fd2514Tadashi G. Takaoka        // shifted or shift locked state.
191ede2133cb137a48aabd2aefc464f68edb7fd2514Tadashi G. Takaoka        return mId.isAlphabetKeyboard() && mId.mElementId != KeyboardId.ELEMENT_ALPHABET;
192571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka    }
193571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka
194ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka    public static boolean isLetterCode(int code) {
1958cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka        return code >= MINIMUM_LETTER_CODE;
196ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka    }
197ee4be6e3c6eed719683fd3019d48365ba76790e2Tadashi G. Takaoka
1984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    public static class Params {
1994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public KeyboardId mId;
2004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mThemeId;
2014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        /** Total height and width of the keyboard, including the paddings and keys */
2034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mOccupiedHeight;
2044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mOccupiedWidth;
2054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        /** Base height and width of the keyboard used to calculate rows' or keys' heights and
2074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         *  widths
2084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         */
2094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mBaseHeight;
2104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mBaseWidth;
2114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mTopPadding;
2134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mBottomPadding;
2144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mHorizontalEdgesPadding;
2154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mHorizontalCenterPadding;
2164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mDefaultRowHeight;
2184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mDefaultKeyWidth;
2194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mHorizontalGap;
2204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mVerticalGap;
2214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMoreKeysTemplate;
2232affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        public int mMaxMoreKeysKeyboardColumn;
2244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int GRID_WIDTH;
2264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int GRID_HEIGHT;
2274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final Set<Key> mKeys = new HashSet<Key>();
2294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final Set<Key> mShiftKeys = new HashSet<Key>();
2304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
231e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok        // TODO: Should be in Key instead of Keyboard.Params?
232e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok        public final Map<Integer, List<Integer>> mAdditionalProximityChars =
233e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                new HashMap<Integer, List<Integer>>();
2344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
23509f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka        public KeyboardSet.KeysCache mKeysCache;
23609f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka
2374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMostCommonKeyHeight = 0;
2384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public int mMostCommonKeyWidth = 0;
2394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public final TouchPositionCorrection mTouchPositionCorrection =
2414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                new TouchPositionCorrection();
2424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static class TouchPositionCorrection {
2444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3;
2454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public boolean mEnabled;
2474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float[] mXs;
2484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float[] mYs;
2494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float[] mRadii;
2504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void load(String[] data) {
2524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int dataLength = data.length;
2534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
2544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (LatinImeLogger.sDBG)
2554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new RuntimeException(
2564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "the size of touch position correction data is invalid");
2574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return;
2584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
2594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
2614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mXs = new float[length];
2624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mYs = new float[length];
2634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRadii = new float[length];
2644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
2654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    for (int i = 0; i < dataLength; ++i) {
2664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE;
2674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
2684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        final float value = Float.parseFloat(data[i]);
2694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (type == 0) {
2704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mXs[index] = value;
2714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        } else if (type == 1) {
2724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mYs[index] = value;
2734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        } else {
2744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mRadii[index] = value;
2754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        }
2764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
2774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } catch (NumberFormatException e) {
2784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (LatinImeLogger.sDBG) {
2794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new RuntimeException(
2804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "the number format for touch position correction data is invalid");
2814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
2824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mXs = null;
2834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mYs = null;
2844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mRadii = null;
2854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
2864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
2874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void setEnabled(boolean enabled) {
2894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mEnabled = enabled;
2904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
2914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public boolean isValid() {
2934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mEnabled && mXs != null && mYs != null && mRadii != null
2944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
2954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
2964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
2974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
2984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected void clearKeys() {
2994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mKeys.clear();
3004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mShiftKeys.clear();
3014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            clearHistogram();
3024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
30409f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka        public void onAddKey(Key newKey) {
30509f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka            final Key key = (mKeysCache != null) ? mKeysCache.get(newKey) : newKey;
3064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mKeys.add(key);
3074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            updateHistogram(key);
3084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (key.mCode == Keyboard.CODE_SHIFT) {
3094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mShiftKeys.add(key);
3104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private int mMaxHeightCount = 0;
3144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private int mMaxWidthCount = 0;
3154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final Map<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>();
3164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final Map<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>();
3174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void clearHistogram() {
3194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMostCommonKeyHeight = 0;
3204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMaxHeightCount = 0;
3214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mHeightHistogram.clear();
3224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMaxWidthCount = 0;
3244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mMostCommonKeyWidth = 0;
3254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mWidthHistogram.clear();
3264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static int updateHistogramCounter(Map<Integer, Integer> histogram, Integer key) {
3294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1;
3304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            histogram.put(key, count);
3314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return count;
3324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void updateHistogram(Key key) {
3354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final Integer height = key.mHeight + key.mVerticalGap;
3364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int heightCount = updateHistogramCounter(mHeightHistogram, height);
3374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (heightCount > mMaxHeightCount) {
3384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMaxHeightCount = heightCount;
3394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMostCommonKeyHeight = height;
3404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final Integer width = key.mWidth + key.mHorizontalGap;
3434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int widthCount = updateHistogramCounter(mWidthHistogram, width);
3444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (widthCount > mMaxWidthCount) {
3454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMaxWidthCount = widthCount;
3464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mMostCommonKeyWidth = width;
3474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
3484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
3494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    }
3504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
3515a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    /**
3520c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka     * Returns the array of the keys that are closest to the given point.
3535a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * @param x the x-coordinate of the point
3545a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * @param y the y-coordinate of the point
3550c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka     * @return the array of the nearest keys to the given point. If the given
3565a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * point is out of range, then an array of size zero is returned.
3575a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     */
3580c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka    public Key[] getNearestKeys(int x, int y) {
3594f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        // Avoid dead pixels at edges of the keyboard
3604f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1));
3614f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
3624f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
3635a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
36463584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka
365e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok    public Map<Integer, List<Integer>> getAdditionalProximityChars() {
366e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok        return mAdditionalProximityChars;
367e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok    }
368e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok
3697dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka    public static String printableCode(int code) {
3707dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        switch (code) {
3717dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_SHIFT: return "shift";
3727dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_SWITCH_ALPHA_SYMBOL: return "symbol";
3738cab0b56eb8db311f158b18a361d9ceb85cff482Tadashi G. Takaoka        case CODE_OUTPUT_TEXT: return "text";
3747dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_DELETE: return "delete";
375a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        case CODE_SETTINGS: return "settings";
3767dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_SHORTCUT: return "shortcut";
3777a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        case CODE_ACTION_ENTER: return "actionEnter";
37805bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka        case CODE_ACTION_NEXT: return "actionNext";
37905bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka        case CODE_ACTION_PREVIOUS: return "actionPrevious";
3807dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        case CODE_UNSPECIFIED: return "unspec";
381ca2f051cc173acc3bce384ebfe08068564bc8e07Tadashi G. Takaoka        case CODE_TAB: return "tab";
382ca2f051cc173acc3bce384ebfe08068564bc8e07Tadashi G. Takaoka        case CODE_ENTER: return "enter";
3837dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        default:
3842a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (code <= 0) Log.w(TAG, "Unknown non-positive key code=" + code);
3852a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (code < CODE_SPACE) return String.format("'\\u%02x'", code);
3862a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            if (code < 0x100) return String.format("'%c'", code);
3872a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka            return String.format("'\\u%04x'", code);
3887dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka        }
3897dfd5a3e833e14d5bf90d728d5a50b40c8a927d2Tadashi G. Takaoka    }
3904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
391411749a4baddc0fda7720deb6e4d67c1f1558cc5Tadashi G. Takaoka   /**
3924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * Keyboard Building helper.
3934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *
3944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * This class parses Keyboard XML file and eventually build a Keyboard.
3954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * The Keyboard XML file looks like:
3964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
3974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/keyboard.xml --&lt;
3984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;Keyboard keyboard_attributes*&lt;
3994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;!-- Keyboard Content --&lt;
4004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Row row_attributes*&lt;
4014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Row Content --&lt;
4024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Key key_attributes* /&lt;
4034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Spacer horizontalGap="0.2in" /&lt;
4044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;include keyboardLayout="@xml/other_keys"&lt;
4054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       ...
4064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/Row&lt;
4074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;include keyboardLayout="@xml/other_rows"&lt;
4084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/Keyboard&lt;
4104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * The XML file which is included in other file must have &gt;merge&lt; as root element,
4124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * such as:
4134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/other_keys.xml --&lt;
4154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;merge&lt;
4164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Key key_attributes* /&lt;
4174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/merge&lt;
4194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * and
4214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/other_rows.xml --&lt;
4234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;merge&lt;
4244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Row row_attributes*&lt;
4254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Key key_attributes* /&lt;
4264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/Row&lt;
4274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/merge&lt;
4294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * You can also use switch-case-default tags to select Rows and Keys.
4314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;switch&lt;
4334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;case case_attribute*&lt;
4344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Any valid tags at switch position --&lt;
4354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/case&lt;
4364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;default&lt;
4384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Any valid tags at switch position --&lt;
4394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/default&lt;
4404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/switch&lt;
4414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * You can declare Key style and specify styles within Key tags.
4434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;switch&lt;
4454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;case mode="email"&lt;
4464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
4474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *           keyLabel=".com"
4484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         /&lt;
4494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;/case&lt;
4504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;case mode="url"&lt;
4514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
4524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *           keyLabel="http://"
4534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         /&lt;
4544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;/case&lt;
4554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/switch&lt;
4564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Key keyStyle="shift-key" ... /&lt;
4584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     */
4604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    public static class Builder<KP extends Params> {
4627a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private static final String BUILDER_TAG = "Keyboard.Builder";
4634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final boolean DEBUG = false;
4644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        // Keyboard XML Tags
4664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_KEYBOARD = "Keyboard";
4674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_ROW = "Row";
4684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_KEY = "Key";
4694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_SPACER = "Spacer";
4704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_INCLUDE = "include";
4714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_MERGE = "merge";
4724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_SWITCH = "switch";
4734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_CASE = "case";
4744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_DEFAULT = "default";
4754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static final String TAG_KEY_STYLE = "key-style";
4764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
4784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final int DEFAULT_KEYBOARD_ROWS = 4;
4794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final KP mParams;
4814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final Context mContext;
4824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final Resources mResources;
4834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final DisplayMetrics mDisplayMetrics;
4844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private int mCurrentY = 0;
4864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Row mCurrentRow = null;
4874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean mLeftEdge;
4884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean mTopEdge;
4894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Key mRightEdgeKey = null;
4904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final KeyStyles mKeyStyles = new KeyStyles();
4914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        /**
4934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
4944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
4954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * defines.
4964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         */
4974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static class Row {
4984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // keyWidth enum constants
4994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_NOT_ENUM = 0;
5004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_FILL_RIGHT = -1;
5014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_FILL_BOTH = -2;
5024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private final Params mParams;
5044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            /** Default width of a key in this row. */
505a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            private float mDefaultKeyWidth;
5064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            /** Default height of a key in this row. */
5074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public final int mRowHeight;
508b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka            /** Default keyLabelFlags in this row. */
509b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka            private int mDefaultKeyLabelFlags;
5104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private final int mCurrentY;
5124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // Will be updated by {@link Key}'s constructor.
5134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private float mCurrentX;
5144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public Row(Resources res, Params params, XmlPullParser parser, int y) {
5164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mParams = params;
5174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
5184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard);
5194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRowHeight = (int)Builder.getDimensionOrFraction(keyboardAttr,
5204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_rowHeight,
5214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseHeight, params.mDefaultRowHeight);
5224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyboardAttr.recycle();
5234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
5244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key);
5254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mDefaultKeyWidth = Builder.getDimensionOrFraction(keyAttr,
5264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth,
5274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseWidth, params.mDefaultKeyWidth);
5284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttr.recycle();
5294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
530b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                mDefaultKeyLabelFlags = 0;
5314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentY = y;
5324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX = 0.0f;
5334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
535a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            public float getDefaultKeyWidth() {
536a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                return mDefaultKeyWidth;
537a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            }
538a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka
539a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            public void setDefaultKeyWidth(float defaultKeyWidth) {
540a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                mDefaultKeyWidth = defaultKeyWidth;
541a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            }
542a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka
543b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka            public int getDefaultKeyLabelFlags() {
544b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                return mDefaultKeyLabelFlags;
545b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka            }
546b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka
547b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka            public void setDefaultKeyLabelFlags(int keyLabelFlags) {
548b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                mDefaultKeyLabelFlags = keyLabelFlags;
549b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka            }
550b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka
5514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void setXPos(float keyXPos) {
5524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX = keyXPos;
5534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void advanceXPos(float width) {
5564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX += width;
5574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public int getKeyY() {
5604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mCurrentY;
5614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float getKeyX(TypedArray keyAttr) {
5644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int widthType = Builder.getEnumValue(keyAttr,
5654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
5664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (widthType == KEYWIDTH_FILL_BOTH) {
5674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillBoth, the key width should start right after the nearest
5684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // key on the left hand side.
5694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return mCurrentX;
5704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
5714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int keyboardRightEdge = mParams.mOccupiedWidth
5734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - mParams.mHorizontalEdgesPadding;
5744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
5754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final float keyXPos = Builder.getDimensionOrFraction(keyAttr,
5764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0);
5774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (keyXPos < 0) {
5784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // If keyXPos is negative, the actual x-coordinate will be
5794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // keyboardWidth + keyXPos.
5804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // keyXPos shouldn't be less than mCurrentX because drawable area for this
5814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // key starts at mCurrentX. Or, this key will overlaps the adjacent key on
5824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // its left hand side.
5834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
5844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
5854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        return keyXPos + mParams.mHorizontalEdgesPadding;
5864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
5874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
5884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mCurrentX;
5894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
591a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            public float getKeyWidth(TypedArray keyAttr) {
592a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                return getKeyWidth(keyAttr, mCurrentX);
593a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            }
594a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka
5954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float getKeyWidth(TypedArray keyAttr, float keyXPos) {
5964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int widthType = Builder.getEnumValue(keyAttr,
5974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
5984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                switch (widthType) {
5994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                case KEYWIDTH_FILL_RIGHT:
6004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                case KEYWIDTH_FILL_BOTH:
6014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final int keyboardRightEdge =
6024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
6034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillRight, the actual key width will be determined to fill
6044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // out the area up to the right edge of the keyboard.
6054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillBoth, the actual key width will be determined to fill out
6064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // the area between the nearest key on the left hand side and the right edge of
6074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // the keyboard.
6084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return keyboardRightEdge - keyXPos;
6094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                default: // KEYWIDTH_NOT_ENUM
6104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return Builder.getDimensionOrFraction(keyAttr,
6114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Key_keyWidth,
6124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mParams.mBaseWidth, mDefaultKeyWidth);
6134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
6144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Builder(Context context, KP params) {
6184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mContext = context;
6194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final Resources res = context.getResources();
6204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mResources = res;
6214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mDisplayMetrics = res.getDisplayMetrics();
6224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams = params;
6244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            setTouchPositionCorrectionData(context, params);
626e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok            setAdditionalProximityChars(context, params);
6274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
6294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
6304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static void setTouchPositionCorrectionData(Context context, Params params) {
6334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = context.obtainStyledAttributes(
6344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    null, R.styleable.Keyboard, R.attr.keyboardStyle, 0);
6354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.mThemeId = a.getInt(R.styleable.Keyboard_themeId, 0);
6364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int resourceId = a.getResourceId(
6374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_touchPositionCorrectionData, 0);
6384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            a.recycle();
6394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (resourceId == 0) {
6404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (LatinImeLogger.sDBG)
6417a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    Log.e(BUILDER_TAG, "touchPositionCorrectionData is not defined");
6424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return;
6434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final String[] data = context.getResources().getStringArray(resourceId);
6464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.mTouchPositionCorrection.load(data);
6474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
649e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok        private static void setAdditionalProximityChars(Context context, Params params) {
650e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok            final String[] additionalChars =
651e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    context.getResources().getStringArray(R.array.additional_proximitychars);
652e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok            int currentPrimaryIndex = 0;
653e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok            for (int i = 0; i < additionalChars.length; ++i) {
654e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                final String additionalChar = additionalChars[i];
655e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                if (TextUtils.isEmpty(additionalChar)) {
656e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    currentPrimaryIndex = 0;
657e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                } else if (currentPrimaryIndex == 0) {
658e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    currentPrimaryIndex = additionalChar.charAt(0);
659e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    params.mAdditionalProximityChars.put(
660e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                            currentPrimaryIndex, new ArrayList<Integer>());
661e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                } else if (currentPrimaryIndex != 0) {
662e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    final int c = additionalChar.charAt(0);
663e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    params.mAdditionalProximityChars.get(currentPrimaryIndex).add(c);
664e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                }
665e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok            }
666e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok        }
667e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok
66809f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka        public void setAutoGenerate(KeyboardSet.KeysCache keysCache) {
66909f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka            mParams.mKeysCache = keysCache;
67009f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka        }
67109f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka
6724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Builder<KP> load(int xmlId, KeyboardId id) {
6734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.mId = id;
6744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final XmlResourceParser parser = mResources.getXml(xmlId);
6754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
6764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboard(parser);
6774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } catch (XmlPullParserException e) {
6787a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
6794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new IllegalArgumentException(e);
6804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } catch (IOException e) {
6817a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
6824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new RuntimeException(e);
6834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
6844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parser.close();
6854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return this;
6874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public void setTouchPositionCorrectionEnabled(boolean enabled) {
6904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.mTouchPositionCorrection.setEnabled(enabled);
6914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Keyboard build() {
6944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return new Keyboard(mParams);
6954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6977a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private int mIndent;
6987a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private static final String SPACES = "                                             ";
6997a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
7007a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private static String spaces(int count) {
7017a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            return (count < SPACES.length()) ? SPACES.substring(0, count) : SPACES;
7027a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
7037a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
7047a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private void startTag(String format, Object ... args) {
7057a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
7067a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
7077a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
7087a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private void endTag(String format, Object ... args) {
7097a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            Log.d(BUILDER_TAG, String.format(spaces(mIndent-- * 2) + format, args));
7107a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
7117a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
7127a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private void startEndTag(String format, Object ... args) {
7137a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
7147a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            mIndent--;
7157a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
7167a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
7174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboard(XmlPullParser parser)
7184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
7197a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId);
7204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
7214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
7224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
7234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
7244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEYBOARD.equals(tag)) {
7254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyboardAttributes(parser);
7264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        startKeyboard();
7274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyboardContent(parser, false);
7284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
7294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
7304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD);
7314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
7324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
7334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
7344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboardAttributes(XmlPullParser parser) {
7374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int displayWidth = mDisplayMetrics.widthPixels;
7384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
7394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
7404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.style.Keyboard);
7414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
7424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Key);
7434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
7444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int displayHeight = mDisplayMetrics.heightPixels;
7454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int keyboardHeight = (int)keyboardAttr.getDimension(
7464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
7474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int maxKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
7484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
7494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                int minKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
7504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2);
7514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (minKeyboardHeight < 0) {
7524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // Specified fraction was negative, so it should be calculated against display
7534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // width.
7544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    minKeyboardHeight = -(int)getDimensionOrFraction(keyboardAttr,
7554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
7564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
7574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Params params = mParams;
7584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Keyboard height will not exceed maxKeyboardHeight and will not be less than
7594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // minKeyboardHeight.
7604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mOccupiedHeight = Math.max(
7614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
7624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mOccupiedWidth = params.mId.mWidth;
7634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mTopPadding = (int)getDimensionOrFraction(keyboardAttr,
7644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0);
7654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr,
7664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0);
7674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mHorizontalEdgesPadding = (int)getDimensionOrFraction(keyboardAttr,
7684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardHorizontalEdgesPadding,
7694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        mParams.mOccupiedWidth, 0);
7704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2
7724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - params.mHorizontalCenterPadding;
7734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mDefaultKeyWidth = (int)getDimensionOrFraction(keyAttr,
7744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth,
7754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS);
7764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr,
7774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0);
7784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr,
7794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0);
7804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding
7814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - params.mBottomPadding + params.mVerticalGap;
7824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr,
7834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_rowHeight, params.mBaseHeight,
7844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
7854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mMoreKeysTemplate = keyboardAttr.getResourceId(
7874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_moreKeysTemplate, 0);
7882affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                params.mMaxMoreKeysKeyboardColumn = keyAttr.getInt(
7894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
7904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mIconsSet.loadIcons(keyboardAttr);
7924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
7934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttr.recycle();
7944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyboardAttr.recycle();
7954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
7964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboardContent(XmlPullParser parser, boolean skip)
7994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
8014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
8024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
8034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_ROW.equals(tag)) {
8054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Row row = parseRowAttributes(parser);
8067a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        if (DEBUG) startTag("<%s>%s", TAG_ROW, skip ? " skipped" : "");
8077a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        if (!skip) {
8084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            startRow(row);
8097a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        }
8104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseRowContent(parser, row, skip);
8114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_INCLUDE.equals(tag)) {
8124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseIncludeKeyboardContent(parser, skip);
8134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SWITCH.equals(tag)) {
8144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSwitchKeyboardContent(parser, skip);
8154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
8164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyStyle(parser, skip);
8174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW);
8194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
8214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8227a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    if (DEBUG) endTag("</%s>", tag);
8234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEYBOARD.equals(tag)) {
8244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        endKeyboard();
8254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
8274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            || TAG_MERGE.equals(tag)) {
8284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
8314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
8334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Row parseRowAttributes(XmlPullParser parser) throws XmlPullParserException {
8374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
8384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard);
8394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
8404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (a.hasValue(R.styleable.Keyboard_horizontalGap))
8414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
8424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (a.hasValue(R.styleable.Keyboard_verticalGap))
8434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
8444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return new Row(mResources, mParams, parser, mCurrentY);
8454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
8464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                a.recycle();
8474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseRowContent(XmlPullParser parser, Row row, boolean skip)
8514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
8534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
8544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
8554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEY.equals(tag)) {
8574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKey(parser, row, skip);
8584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SPACER.equals(tag)) {
8594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSpacer(parser, row, skip);
8604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_INCLUDE.equals(tag)) {
8614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseIncludeRowContent(parser, row, skip);
8624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SWITCH.equals(tag)) {
8634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSwitchRowContent(parser, row, skip);
8644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
8654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyStyle(parser, skip);
8664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
8684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
8704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8717a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    if (DEBUG) endTag("</%s>", tag);
8724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_ROW.equals(tag)) {
8737a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        if (!skip) {
8744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            endRow(row);
8757a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        }
8764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
8784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            || TAG_MERGE.equals(tag)) {
8794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
8824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
8844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKey(XmlPullParser parser, Row row, boolean skip)
8884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
8904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_KEY, parser);
8917a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY);
8924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
8934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Key key = new Key(mResources, mParams, row, parser, mKeyStyles);
8947a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) {
8957a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY,
8967a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            (key.isEnabled() ? "" : " disabled"), key,
8977a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            Arrays.toString(key.mMoreKeys));
8987a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                }
8994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_KEY, parser);
9004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                endKey(key);
9014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSpacer(XmlPullParser parser, Row row, boolean skip)
9054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
9074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_SPACER, parser);
9087a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) startEndTag("<%s /> skipped", TAG_SPACER);
9094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
9104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Key.Spacer spacer = new Key.Spacer(
9114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        mResources, mParams, row, parser, mKeyStyles);
9127a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) startEndTag("<%s />", TAG_SPACER);
9134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_SPACER, parser);
9144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                endKey(spacer);
9154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeKeyboardContent(XmlPullParser parser, boolean skip)
9194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseIncludeInternal(parser, null, skip);
9214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeRowContent(XmlPullParser parser, Row row, boolean skip)
9244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseIncludeInternal(parser, row, skip);
9264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip)
9294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
9314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
9327a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) startEndTag("</%s> skipped", TAG_INCLUDE);
9334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
934a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                final AttributeSet attr = Xml.asAttributeSet(parser);
935a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                final TypedArray keyboardAttr = mResources.obtainAttributes(attr,
9364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Include);
937a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                final TypedArray keyAttr = mResources.obtainAttributes(attr,
938a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        R.styleable.Keyboard_Key);
9394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                int keyboardLayout = 0;
940a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                float savedDefaultKeyWidth = 0;
941b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                int savedDefaultKeyLabelFlags = 0;
9424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
943a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    XmlParseUtils.checkAttributeExists(keyboardAttr,
9444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
9454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            TAG_INCLUDE, parser);
946a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    keyboardLayout = keyboardAttr.getResourceId(
9474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Include_keyboardLayout, 0);
948a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    if (row != null) {
949a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        savedDefaultKeyWidth = row.getDefaultKeyWidth();
950b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                        savedDefaultKeyLabelFlags = row.getDefaultKeyLabelFlags();
951a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
952a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                            // Override current x coordinate.
953a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                            row.setXPos(row.getKeyX(keyAttr));
954a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        }
955a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyWidth)) {
956a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                            // Override default key width.
957a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                            row.setDefaultKeyWidth(row.getKeyWidth(keyAttr));
958a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        }
959b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                        if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyLabelFlags)) {
960b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                            // Override default key label flags.
961b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                            row.setDefaultKeyLabelFlags(
962b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                                    keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0)
963b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                                    | savedDefaultKeyLabelFlags);
964b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                        }
965a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    }
9664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } finally {
967a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    keyboardAttr.recycle();
968a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    keyAttr.recycle();
9694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
9727a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) {
9737a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE,
9747a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            mResources.getResourceEntryName(keyboardLayout));
9757a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                }
9764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
9774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
9784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    parseMerge(parserForInclude, row, skip);
9794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } finally {
980a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    if (row != null) {
981b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                        // Restore default key width and key label flags.
982a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        row.setDefaultKeyWidth(savedDefaultKeyWidth);
983b03529aa0119d38054fe825156669c45f7a6c8c3Tadashi G. Takaoka                        row.setDefaultKeyLabelFlags(savedDefaultKeyLabelFlags);
984a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    }
9854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    parserForInclude.close();
9864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseMerge(XmlPullParser parser, Row row, boolean skip)
9914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9927a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            if (DEBUG) startTag("<%s>", TAG_MERGE);
9934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
9944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
9954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
9964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
9974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_MERGE.equals(tag)) {
9984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (row == null) {
9994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            parseKeyboardContent(parser, skip);
10004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        } else {
10014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            parseRowContent(parser, row, skip);
10024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        }
10034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
10044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
10054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.ParseException(
10064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "Included keyboard layout must have <merge> root element", parser);
10074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
10084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
10094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchKeyboardContent(XmlPullParser parser, boolean skip)
10134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
10144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseSwitchInternal(parser, null, skip);
10154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchRowContent(XmlPullParser parser, Row row, boolean skip)
10184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
10194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseSwitchInternal(parser, row, skip);
10204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip)
10234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
10247a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId);
10254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            boolean selected = false;
10264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
10274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
10284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
10294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
10304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_CASE.equals(tag)) {
10314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        selected |= parseCase(parser, row, selected ? true : skip);
10324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_DEFAULT.equals(tag)) {
10334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        selected |= parseDefault(parser, row, selected ? true : skip);
10344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
10354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
10364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
10374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
10384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
10394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_SWITCH.equals(tag)) {
10407a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        if (DEBUG) endTag("</%s>", TAG_SWITCH);
10414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
10424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
10434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
10444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
10454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
10464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseCase(XmlPullParser parser, Row row, boolean skip)
10504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
10514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final boolean selected = parseCaseCondition(parser);
10524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (row == null) {
10534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Processing Rows.
10544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboardContent(parser, selected ? skip : true);
10554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
10564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Processing Keys.
10574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseRowContent(parser, row, selected ? skip : true);
10584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return selected;
10604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseCaseCondition(XmlPullParser parser) {
10634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final KeyboardId id = mParams.mId;
10644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (id == null)
10654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return true;
10664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
10684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Case);
10694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
107083306b914f36871f785677ca13e1ce8033774b79Tadashi G. Takaoka                final boolean keyboardSetElementMatched = matchTypedValue(a,
107183306b914f36871f785677ca13e1ce8033774b79Tadashi G. Takaoka                        R.styleable.Keyboard_Case_keyboardSetElement, id.mElementId,
107283306b914f36871f785677ca13e1ce8033774b79Tadashi G. Takaoka                        KeyboardId.elementIdToName(id.mElementId));
10734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean modeMatched = matchTypedValue(a,
10744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
107505bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                final boolean navigateNextMatched = matchBoolean(a,
107605bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                        R.styleable.Keyboard_Case_navigateNext, id.navigateNext());
107705bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                final boolean navigatePreviousMatched = matchBoolean(a,
107805bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                        R.styleable.Keyboard_Case_navigatePrevious, id.navigatePrevious());
10794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean passwordInputMatched = matchBoolean(a,
10804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
10814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean clobberSettingsKeyMatched = matchBoolean(a,
10824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
10834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean shortcutKeyEnabledMatched = matchBoolean(a,
10844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
10854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean hasShortcutKeyMatched = matchBoolean(a,
10864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
108781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                final boolean languageSwitchKeyEnabledMatched = matchBoolean(a,
108881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                        R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
108981d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                        id.mLanguageSwitchKeyEnabled);
10907a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                final boolean isMultiLineMatched = matchBoolean(a,
10917a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine());
10924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean imeActionMatched = matchInteger(a,
10934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_imeAction, id.imeAction());
10944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean localeCodeMatched = matchString(a,
10954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
10964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean languageCodeMatched = matchString(a,
10974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
10984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean countryCodeMatched = matchString(a,
10994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
110083306b914f36871f785677ca13e1ce8033774b79Tadashi G. Takaoka                final boolean selected = keyboardSetElementMatched && modeMatched
110105bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                        && navigateNextMatched && navigatePreviousMatched && passwordInputMatched
110297bde43740d8ba7f8aedde35d20621823140966cTadashi G. Takaoka                        && clobberSettingsKeyMatched && shortcutKeyEnabledMatched
110381d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                        && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched
110481d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                        && isMultiLineMatched && imeActionMatched && localeCodeMatched
110581d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                        && languageCodeMatched && countryCodeMatched;
11067a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
11077a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) {
110881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                    startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
11097a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_keyboardSetElement),
11107a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "keyboardSetElement"),
11117a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
111205bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_imeAction),
111305bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                                    "imeAction"),
111405bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_navigateNext,
111505bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                                    "navigateNext"),
111605bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_navigatePrevious,
111705bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                                    "navigatePrevious"),
11187a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
11197a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "clobberSettingsKey"),
112005bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_passwordInput,
112105bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                                    "passwordInput"),
11227a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
11237a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "shortcutKeyEnabled"),
11247a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey,
11257a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "hasShortcutKey"),
112681d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
112781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                                    "languageSwitchKeyEnabled"),
11287a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_isMultiLine,
11297a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "isMultiLine"),
11307a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_localeCode),
11317a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "localeCode"),
11327a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
11337a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "languageCode"),
11347a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_countryCode),
11357a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "countryCode"),
11367a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            selected ? "" : " skipped");
11377a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                }
11384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return selected;
11404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
11414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                a.recycle();
11424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchInteger(TypedArray a, int index, int value) {
11464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index) || a.getInt(index, 0) == value;
11494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchBoolean(TypedArray a, int index, boolean value) {
11524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index) || a.getBoolean(index, false) == value;
11554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchString(TypedArray a, int index, String value) {
11584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index)
11614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    || stringArrayContains(a.getString(index).split("\\|"), value);
11624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchTypedValue(TypedArray a, int index, int intValue,
11654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                String strValue) {
11664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue v = a.peekValue(index);
11694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (v == null)
11704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return true;
11714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isIntegerValue(v)) {
11734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return intValue == a.getInt(index, 0);
11744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else if (isStringValue(v)) {
11754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return stringArrayContains(a.getString(index).split("\\|"), strValue);
11764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return false;
11784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean stringArrayContains(String[] array, String value) {
11814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            for (final String elem : array) {
11824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (elem.equals(value))
11834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return true;
11844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return false;
11864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseDefault(XmlPullParser parser, Row row, boolean skip)
11894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
11907a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            if (DEBUG) startTag("<%s>", TAG_DEFAULT);
11914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (row == null) {
11924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboardContent(parser, skip);
11934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
11944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseRowContent(parser, row, skip);
11954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return true;
11974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyStyle(XmlPullParser parser, boolean skip)
12007a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                throws XmlPullParserException, IOException {
12014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
12024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_KeyStyle);
12034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
12044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Key);
12054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
12064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
12074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
12084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            + "/> needs styleName attribute", parser);
12097a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) {
12107a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE,
12117a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
12127a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        skip ? " skipped" : "");
12137a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                }
12144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (!skip)
12154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
12164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
12174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyStyleAttr.recycle();
12184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttrs.recycle();
12194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12207a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            XmlParseUtils.checkEndTag(TAG_KEY_STYLE, parser);
12214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void startKeyboard() {
12244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentY += mParams.mTopPadding;
12254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mTopEdge = true;
12264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void startRow(Row row) {
12294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
12304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentRow = row;
12314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mLeftEdge = true;
12324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = null;
12334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endRow(Row row) {
12364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mCurrentRow == null)
12374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new InflateException("orphant end row tag");
12384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mRightEdgeKey != null) {
12394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRightEdgeKey.markAsRightEdge(mParams);
12404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRightEdgeKey = null;
12414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
12434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentY += row.mRowHeight;
12444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentRow = null;
12454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mTopEdge = false;
12464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endKey(Key key) {
12494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.onAddKey(key);
12504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mLeftEdge) {
12514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                key.markAsLeftEdge(mParams);
12524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mLeftEdge = false;
12534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mTopEdge) {
12554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                key.markAsTopEdge(mParams);
12564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = key;
12584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endKeyboard() {
12614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // nothing to do here.
12624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void addEdgeSpace(float width, Row row) {
12654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            row.advanceXPos(width);
12664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mLeftEdge = false;
12674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = null;
12684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static float getDimensionOrFraction(TypedArray a, int index, int base,
12714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                float defValue) {
12724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue value = a.peekValue(index);
12734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (value == null)
12744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return defValue;
12754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isFractionValue(value)) {
12764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getFraction(index, base, base, defValue);
12774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else if (isDimensionValue(value)) {
12784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getDimension(index, defValue);
12794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return defValue;
12814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static int getEnumValue(TypedArray a, int index, int defValue) {
12844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue value = a.peekValue(index);
12854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (value == null)
12864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return defValue;
12874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isIntegerValue(value)) {
12884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getInt(index, defValue);
12894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return defValue;
12914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isFractionValue(TypedValue v) {
12944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_FRACTION;
12954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isDimensionValue(TypedValue v) {
12984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_DIMENSION;
12994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
13004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
13014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isIntegerValue(TypedValue v) {
13024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
13034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
13044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
13054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isStringValue(TypedValue v) {
13064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_STRING;
13074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
13084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
13094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static String textAttr(String value, String name) {
13104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return value != null ? String.format(" %s=%s", name, value) : "";
13114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
13124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
13134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static String booleanAttr(TypedArray a, int index, String name) {
13144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return a.hasValue(index)
13154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    ? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
13164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
13174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    }
13185a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka}
1319