Keyboard.java revision 81d4e3cd66a9388c47c7dba55240ddf849b31934
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
1570d5494c66ac3e5947040e8148091163a1c8716f7satok        mProximityInfo = new ProximityInfo(
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
3914087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka    public static String toThemeName(int themeId) {
3924087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        // This should be aligned with theme-*.xml resource files' themeId attribute.
3934087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        switch (themeId) {
3944087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 0: return "Basic";
3954087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 1: return "BasicHighContrast";
3964087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 5: return "IceCreamSandwich";
3974087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 6: return "Stone";
3984087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 7: return "StoneBold";
3994087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        case 8: return "GingerBread";
4004087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        default: return null;
4014087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka        }
4024087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka    }
4034087ba69faec0dd89fae7d53d7527448091e0aadTadashi G. Takaoka
4044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    /**
4054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * Keyboard Building helper.
4064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *
4074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * This class parses Keyboard XML file and eventually build a Keyboard.
4084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * The Keyboard XML file looks like:
4094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/keyboard.xml --&lt;
4114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;Keyboard keyboard_attributes*&lt;
4124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;!-- Keyboard Content --&lt;
4134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Row row_attributes*&lt;
4144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Row Content --&lt;
4154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Key key_attributes* /&lt;
4164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Spacer horizontalGap="0.2in" /&lt;
4174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;include keyboardLayout="@xml/other_keys"&lt;
4184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       ...
4194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/Row&lt;
4204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;include keyboardLayout="@xml/other_rows"&lt;
4214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/Keyboard&lt;
4234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * The XML file which is included in other file must have &gt;merge&lt; as root element,
4254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * such as:
4264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/other_keys.xml --&lt;
4284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;merge&lt;
4294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Key key_attributes* /&lt;
4304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/merge&lt;
4324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * and
4344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;!-- xml/other_rows.xml --&lt;
4364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;merge&lt;
4374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Row row_attributes*&lt;
4384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;Key key_attributes* /&lt;
4394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/Row&lt;
4404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/merge&lt;
4424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * You can also use switch-case-default tags to select Rows and Keys.
4444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;switch&lt;
4464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;case case_attribute*&lt;
4474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Any valid tags at switch position --&lt;
4484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/case&lt;
4494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;default&lt;
4514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;!-- Any valid tags at switch position --&lt;
4524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/default&lt;
4534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *   &gt;/switch&lt;
4544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * You can declare Key style and specify styles within Key tags.
4564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * <pre>
4574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;switch&lt;
4584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;case mode="email"&lt;
4594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
4604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *           keyLabel=".com"
4614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         /&lt;
4624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;/case&lt;
4634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;case mode="url"&lt;
4644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         &gt;key-style styleName="f1-key" parentStyle="modifier-key"
4654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *           keyLabel="http://"
4664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *         /&lt;
4674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *       &gt;/case&lt;
4684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;/switch&lt;
4694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     ...
4704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     *     &gt;Key keyStyle="shift-key" ... /&lt;
4714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     * </pre>
4724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka     */
4734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    public static class Builder<KP extends Params> {
4757a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private static final String BUILDER_TAG = "Keyboard.Builder";
4764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final boolean DEBUG = false;
4774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        // Keyboard XML Tags
4794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_KEYBOARD = "Keyboard";
4804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_ROW = "Row";
4814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_KEY = "Key";
4824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_SPACER = "Spacer";
4834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_INCLUDE = "include";
4844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_MERGE = "merge";
4854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_SWITCH = "switch";
4864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_CASE = "case";
4874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final String TAG_DEFAULT = "default";
4884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static final String TAG_KEY_STYLE = "key-style";
4894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
4914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static final int DEFAULT_KEYBOARD_ROWS = 4;
4924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final KP mParams;
4944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final Context mContext;
4954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        protected final Resources mResources;
4964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final DisplayMetrics mDisplayMetrics;
4974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
4984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private int mCurrentY = 0;
4994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Row mCurrentRow = null;
5004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean mLeftEdge;
5014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean mTopEdge;
5024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Key mRightEdgeKey = null;
5034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private final KeyStyles mKeyStyles = new KeyStyles();
5044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        /**
5064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
5074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
5084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         * defines.
5094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka         */
5104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static class Row {
5114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // keyWidth enum constants
5124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_NOT_ENUM = 0;
5134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_FILL_RIGHT = -1;
5144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private static final int KEYWIDTH_FILL_BOTH = -2;
5154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private final Params mParams;
5174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            /** Default width of a key in this row. */
518a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            private float mDefaultKeyWidth;
5194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            /** Default height of a key in this row. */
5204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public final int mRowHeight;
5214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private final int mCurrentY;
5234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // Will be updated by {@link Key}'s constructor.
5244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            private float mCurrentX;
5254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public Row(Resources res, Params params, XmlPullParser parser, int y) {
5274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mParams = params;
5284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
5294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard);
5304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRowHeight = (int)Builder.getDimensionOrFraction(keyboardAttr,
5314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_rowHeight,
5324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseHeight, params.mDefaultRowHeight);
5334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyboardAttr.recycle();
5344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
5354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key);
5364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mDefaultKeyWidth = Builder.getDimensionOrFraction(keyAttr,
5374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth,
5384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseWidth, params.mDefaultKeyWidth);
5394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttr.recycle();
5404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentY = y;
5424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX = 0.0f;
5434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
545a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            public float getDefaultKeyWidth() {
546a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                return mDefaultKeyWidth;
547a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            }
548a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka
549a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            public void setDefaultKeyWidth(float defaultKeyWidth) {
550a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                mDefaultKeyWidth = defaultKeyWidth;
551a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            }
552a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka
5534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void setXPos(float keyXPos) {
5544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX = keyXPos;
5554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public void advanceXPos(float width) {
5584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mCurrentX += width;
5594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public int getKeyY() {
5624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mCurrentY;
5634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float getKeyX(TypedArray keyAttr) {
5664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int widthType = Builder.getEnumValue(keyAttr,
5674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
5684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (widthType == KEYWIDTH_FILL_BOTH) {
5694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillBoth, the key width should start right after the nearest
5704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // key on the left hand side.
5714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return mCurrentX;
5724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
5734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
5744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int keyboardRightEdge = mParams.mOccupiedWidth
5754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - mParams.mHorizontalEdgesPadding;
5764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
5774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final float keyXPos = Builder.getDimensionOrFraction(keyAttr,
5784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0);
5794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (keyXPos < 0) {
5804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // If keyXPos is negative, the actual x-coordinate will be
5814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // keyboardWidth + keyXPos.
5824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // keyXPos shouldn't be less than mCurrentX because drawable area for this
5834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // key starts at mCurrentX. Or, this key will overlaps the adjacent key on
5844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        // its left hand side.
5854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
5864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
5874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        return keyXPos + mParams.mHorizontalEdgesPadding;
5884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
5894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
5904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return mCurrentX;
5914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
5924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
593a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            public float getKeyWidth(TypedArray keyAttr) {
594a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                return getKeyWidth(keyAttr, mCurrentX);
595a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka            }
596a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka
5974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            public float getKeyWidth(TypedArray keyAttr, float keyXPos) {
5984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int widthType = Builder.getEnumValue(keyAttr,
5994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
6004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                switch (widthType) {
6014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                case KEYWIDTH_FILL_RIGHT:
6024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                case KEYWIDTH_FILL_BOTH:
6034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final int keyboardRightEdge =
6044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
6054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillRight, the actual key width will be determined to fill
6064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // out the area up to the right edge of the keyboard.
6074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // If keyWidth is fillBoth, the actual key width will be determined to fill out
6084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // the area between the nearest key on the left hand side and the right edge of
6094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // the keyboard.
6104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return keyboardRightEdge - keyXPos;
6114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                default: // KEYWIDTH_NOT_ENUM
6124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return Builder.getDimensionOrFraction(keyAttr,
6134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Key_keyWidth,
6144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            mParams.mBaseWidth, mDefaultKeyWidth);
6154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
6164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Builder(Context context, KP params) {
6204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mContext = context;
6214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final Resources res = context.getResources();
6224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mResources = res;
6234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mDisplayMetrics = res.getDisplayMetrics();
6244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams = params;
6264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            setTouchPositionCorrectionData(context, params);
628e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok            setAdditionalProximityChars(context, params);
6294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
6314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
6324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static void setTouchPositionCorrectionData(Context context, Params params) {
6354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = context.obtainStyledAttributes(
6364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    null, R.styleable.Keyboard, R.attr.keyboardStyle, 0);
6374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.mThemeId = a.getInt(R.styleable.Keyboard_themeId, 0);
6384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int resourceId = a.getResourceId(
6394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_touchPositionCorrectionData, 0);
6404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            a.recycle();
6414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (resourceId == 0) {
6424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (LatinImeLogger.sDBG)
6437a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    Log.e(BUILDER_TAG, "touchPositionCorrectionData is not defined");
6444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return;
6454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final String[] data = context.getResources().getStringArray(resourceId);
6484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            params.mTouchPositionCorrection.load(data);
6494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
651e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok        private static void setAdditionalProximityChars(Context context, Params params) {
652e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok            final String[] additionalChars =
653e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    context.getResources().getStringArray(R.array.additional_proximitychars);
654e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok            int currentPrimaryIndex = 0;
655e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok            for (int i = 0; i < additionalChars.length; ++i) {
656e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                final String additionalChar = additionalChars[i];
657e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                if (TextUtils.isEmpty(additionalChar)) {
658e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    currentPrimaryIndex = 0;
659e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                } else if (currentPrimaryIndex == 0) {
660e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    currentPrimaryIndex = additionalChar.charAt(0);
661e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    params.mAdditionalProximityChars.put(
662e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                            currentPrimaryIndex, new ArrayList<Integer>());
663e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                } else if (currentPrimaryIndex != 0) {
664e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    final int c = additionalChar.charAt(0);
665e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                    params.mAdditionalProximityChars.get(currentPrimaryIndex).add(c);
666e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok                }
667e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok            }
668e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok        }
669e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok
67009f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka        public void setAutoGenerate(KeyboardSet.KeysCache keysCache) {
67109f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka            mParams.mKeysCache = keysCache;
67209f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka        }
67309f8b126e532ca2ac6bbe00c0d78bf03e44b78a2Tadashi G. Takaoka
6744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Builder<KP> load(int xmlId, KeyboardId id) {
6754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.mId = id;
6764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final XmlResourceParser parser = mResources.getXml(xmlId);
6774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
6784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboard(parser);
6794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } catch (XmlPullParserException e) {
6807a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
6814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new IllegalArgumentException(e);
6824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } catch (IOException e) {
6837a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
6844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new RuntimeException(e);
6854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
6864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parser.close();
6874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
6884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return this;
6894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public void setTouchPositionCorrectionEnabled(boolean enabled) {
6924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.mTouchPositionCorrection.setEnabled(enabled);
6934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public Keyboard build() {
6964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return new Keyboard(mParams);
6974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
6984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
6997a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private int mIndent;
7007a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private static final String SPACES = "                                             ";
7017a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
7027a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private static String spaces(int count) {
7037a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            return (count < SPACES.length()) ? SPACES.substring(0, count) : SPACES;
7047a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
7057a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
7067a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private void startTag(String format, Object ... args) {
7077a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
7087a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
7097a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
7107a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private void endTag(String format, Object ... args) {
7117a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            Log.d(BUILDER_TAG, String.format(spaces(mIndent-- * 2) + format, args));
7127a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
7137a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
7147a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        private void startEndTag(String format, Object ... args) {
7157a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
7167a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            mIndent--;
7177a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka        }
7187a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
7194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboard(XmlPullParser parser)
7204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
7217a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId);
7224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
7234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
7244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
7254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
7264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEYBOARD.equals(tag)) {
7274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyboardAttributes(parser);
7284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        startKeyboard();
7294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyboardContent(parser, false);
7304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
7314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
7324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD);
7334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
7344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
7354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
7364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboardAttributes(XmlPullParser parser) {
7394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final int displayWidth = mDisplayMetrics.widthPixels;
7404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
7414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
7424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.style.Keyboard);
7434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
7444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Key);
7454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
7464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int displayHeight = mDisplayMetrics.heightPixels;
7474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int keyboardHeight = (int)keyboardAttr.getDimension(
7484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
7494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final int maxKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
7504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
7514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                int minKeyboardHeight = (int)getDimensionOrFraction(keyboardAttr,
7524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2);
7534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (minKeyboardHeight < 0) {
7544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // Specified fraction was negative, so it should be calculated against display
7554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    // width.
7564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    minKeyboardHeight = -(int)getDimensionOrFraction(keyboardAttr,
7574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
7584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
7594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Params params = mParams;
7604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Keyboard height will not exceed maxKeyboardHeight and will not be less than
7614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // minKeyboardHeight.
7624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mOccupiedHeight = Math.max(
7634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
7644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mOccupiedWidth = params.mId.mWidth;
7654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mTopPadding = (int)getDimensionOrFraction(keyboardAttr,
7664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0);
7674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr,
7684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0);
7694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mHorizontalEdgesPadding = (int)getDimensionOrFraction(keyboardAttr,
7704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_keyboardHorizontalEdgesPadding,
7714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        mParams.mOccupiedWidth, 0);
7724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2
7744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - params.mHorizontalCenterPadding;
7754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mDefaultKeyWidth = (int)getDimensionOrFraction(keyAttr,
7764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth,
7774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS);
7784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr,
7794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0);
7804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr,
7814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0);
7824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding
7834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        - params.mBottomPadding + params.mVerticalGap;
7844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr,
7854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_rowHeight, params.mBaseHeight,
7864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
7874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mMoreKeysTemplate = keyboardAttr.getResourceId(
7894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_moreKeysTemplate, 0);
7902affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                params.mMaxMoreKeysKeyboardColumn = keyAttr.getInt(
7914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
7924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
7934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                params.mIconsSet.loadIcons(keyboardAttr);
7944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
7954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttr.recycle();
7964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyboardAttr.recycle();
7974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
7984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
7994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyboardContent(XmlPullParser parser, boolean skip)
8014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
8034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
8044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
8054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_ROW.equals(tag)) {
8074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        Row row = parseRowAttributes(parser);
8087a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        if (DEBUG) startTag("<%s>%s", TAG_ROW, skip ? " skipped" : "");
8097a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        if (!skip) {
8104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            startRow(row);
8117a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        }
8124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseRowContent(parser, row, skip);
8134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_INCLUDE.equals(tag)) {
8144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseIncludeKeyboardContent(parser, skip);
8154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SWITCH.equals(tag)) {
8164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSwitchKeyboardContent(parser, skip);
8174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
8184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyStyle(parser, skip);
8194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW);
8214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
8234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8247a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    if (DEBUG) endTag("</%s>", tag);
8254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEYBOARD.equals(tag)) {
8264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        endKeyboard();
8274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
8294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            || TAG_MERGE.equals(tag)) {
8304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
8334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
8354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private Row parseRowAttributes(XmlPullParser parser) throws XmlPullParserException {
8394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
8404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard);
8414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
8424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (a.hasValue(R.styleable.Keyboard_horizontalGap))
8434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
8444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (a.hasValue(R.styleable.Keyboard_verticalGap))
8454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
8464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return new Row(mResources, mParams, parser, mCurrentY);
8474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
8484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                a.recycle();
8494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseRowContent(XmlPullParser parser, Row row, boolean skip)
8534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
8554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
8564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
8574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_KEY.equals(tag)) {
8594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKey(parser, row, skip);
8604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SPACER.equals(tag)) {
8614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSpacer(parser, row, skip);
8624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_INCLUDE.equals(tag)) {
8634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseIncludeRowContent(parser, row, skip);
8644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_SWITCH.equals(tag)) {
8654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseSwitchRowContent(parser, row, skip);
8664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_KEY_STYLE.equals(tag)) {
8674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        parseKeyStyle(parser, skip);
8684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
8704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
8724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
8737a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    if (DEBUG) endTag("</%s>", tag);
8744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_ROW.equals(tag)) {
8757a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        if (!skip) {
8764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            endRow(row);
8777a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        }
8784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
8804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            || TAG_MERGE.equals(tag)) {
8814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
8824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
8834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
8844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
8854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
8864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
8874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
8884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
8894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKey(XmlPullParser parser, Row row, boolean skip)
8904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
8914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
8924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_KEY, parser);
8937a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY);
8944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
8954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Key key = new Key(mResources, mParams, row, parser, mKeyStyles);
8967a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) {
8977a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY,
8987a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            (key.isEnabled() ? "" : " disabled"), key,
8997a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            Arrays.toString(key.mMoreKeys));
9007a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                }
9014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_KEY, parser);
9024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                endKey(key);
9034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSpacer(XmlPullParser parser, Row row, boolean skip)
9074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
9094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_SPACER, parser);
9107a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) startEndTag("<%s /> skipped", TAG_SPACER);
9114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
9124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final Key.Spacer spacer = new Key.Spacer(
9134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        mResources, mParams, row, parser, mKeyStyles);
9147a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) startEndTag("<%s />", TAG_SPACER);
9154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_SPACER, parser);
9164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                endKey(spacer);
9174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeKeyboardContent(XmlPullParser parser, boolean skip)
9214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseIncludeInternal(parser, null, skip);
9234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeRowContent(XmlPullParser parser, Row row, boolean skip)
9264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseIncludeInternal(parser, row, skip);
9284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip)
9314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (skip) {
9334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
9347a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) startEndTag("</%s> skipped", TAG_INCLUDE);
9354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
936a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                final AttributeSet attr = Xml.asAttributeSet(parser);
937a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                final TypedArray keyboardAttr = mResources.obtainAttributes(attr,
9384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Include);
939a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                final TypedArray keyAttr = mResources.obtainAttributes(attr,
940a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        R.styleable.Keyboard_Key);
9414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                int keyboardLayout = 0;
942a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                float savedDefaultKeyWidth = 0;
9434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
944a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    XmlParseUtils.checkAttributeExists(keyboardAttr,
9454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
9464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            TAG_INCLUDE, parser);
947a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    keyboardLayout = keyboardAttr.getResourceId(
9484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            R.styleable.Keyboard_Include_keyboardLayout, 0);
949a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    if (row != null) {
950a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        savedDefaultKeyWidth = row.getDefaultKeyWidth();
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                        }
959a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    }
9604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } finally {
961a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    keyboardAttr.recycle();
962a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    keyAttr.recycle();
9634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
9667a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) {
9677a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE,
9687a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            mResources.getResourceEntryName(keyboardLayout));
9697a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                }
9704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
9714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                try {
9724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    parseMerge(parserForInclude, row, skip);
9734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } finally {
974a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    if (row != null) {
975a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        // Restore default key width.
976a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                        row.setDefaultKeyWidth(savedDefaultKeyWidth);
977a1e370ee4fd033fae0f783d9120f6ab75d1ce711Tadashi G. Takaoka                    }
9784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    parserForInclude.close();
9794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
9804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
9814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
9824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
9834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseMerge(XmlPullParser parser, Row row, boolean skip)
9844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
9857a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            if (DEBUG) startTag("<%s>", TAG_MERGE);
9864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
9874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
9884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
9894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
9904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_MERGE.equals(tag)) {
9914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        if (row == null) {
9924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            parseKeyboardContent(parser, skip);
9934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        } else {
9944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            parseRowContent(parser, row, skip);
9954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        }
9964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
9974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
9984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.ParseException(
9994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                                "Included keyboard layout must have <merge> root element", parser);
10004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
10014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
10024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchKeyboardContent(XmlPullParser parser, boolean skip)
10064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
10074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseSwitchInternal(parser, null, skip);
10084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchRowContent(XmlPullParser parser, Row row, boolean skip)
10114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
10124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            parseSwitchInternal(parser, row, skip);
10134e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip)
10164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
10177a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId);
10184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            boolean selected = false;
10194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            int event;
10204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
10214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (event == XmlPullParser.START_TAG) {
10224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
10234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_CASE.equals(tag)) {
10244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        selected |= parseCase(parser, row, selected ? true : skip);
10254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else if (TAG_DEFAULT.equals(tag)) {
10264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        selected |= parseDefault(parser, row, selected ? true : skip);
10274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
10284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
10294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
10304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                } else if (event == XmlPullParser.END_TAG) {
10314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    final String tag = parser.getName();
10324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    if (TAG_SWITCH.equals(tag)) {
10337a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        if (DEBUG) endTag("</%s>", TAG_SWITCH);
10344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        break;
10354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    } else {
10364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
10374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    }
10384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                }
10394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseCase(XmlPullParser parser, Row row, boolean skip)
10434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
10444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final boolean selected = parseCaseCondition(parser);
10454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (row == null) {
10464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Processing Rows.
10474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboardContent(parser, selected ? skip : true);
10484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
10494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                // Processing Keys.
10504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseRowContent(parser, row, selected ? skip : true);
10514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
10524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return selected;
10534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
10544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseCaseCondition(XmlPullParser parser) {
10564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final KeyboardId id = mParams.mId;
10574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (id == null)
10584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return true;
10594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
10604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
10614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Case);
10624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
106383306b914f36871f785677ca13e1ce8033774b79Tadashi G. Takaoka                final boolean keyboardSetElementMatched = matchTypedValue(a,
106483306b914f36871f785677ca13e1ce8033774b79Tadashi G. Takaoka                        R.styleable.Keyboard_Case_keyboardSetElement, id.mElementId,
106583306b914f36871f785677ca13e1ce8033774b79Tadashi G. Takaoka                        KeyboardId.elementIdToName(id.mElementId));
10664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean modeMatched = matchTypedValue(a,
10674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
106805bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                final boolean navigateNextMatched = matchBoolean(a,
106905bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                        R.styleable.Keyboard_Case_navigateNext, id.navigateNext());
107005bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                final boolean navigatePreviousMatched = matchBoolean(a,
107105bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                        R.styleable.Keyboard_Case_navigatePrevious, id.navigatePrevious());
10724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean passwordInputMatched = matchBoolean(a,
10734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
10744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean clobberSettingsKeyMatched = matchBoolean(a,
10754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
10764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean shortcutKeyEnabledMatched = matchBoolean(a,
10774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
10784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean hasShortcutKeyMatched = matchBoolean(a,
10794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
108081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                final boolean languageSwitchKeyEnabledMatched = matchBoolean(a,
108181d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                        R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
108281d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                        id.mLanguageSwitchKeyEnabled);
10837a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                final boolean isMultiLineMatched = matchBoolean(a,
10847a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine());
10854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean imeActionMatched = matchInteger(a,
10864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_imeAction, id.imeAction());
10874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean localeCodeMatched = matchString(a,
10884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
10894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean languageCodeMatched = matchString(a,
10904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
10914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                final boolean countryCodeMatched = matchString(a,
10924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                        R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
109383306b914f36871f785677ca13e1ce8033774b79Tadashi G. Takaoka                final boolean selected = keyboardSetElementMatched && modeMatched
109405bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                        && navigateNextMatched && navigatePreviousMatched && passwordInputMatched
109597bde43740d8ba7f8aedde35d20621823140966cTadashi G. Takaoka                        && clobberSettingsKeyMatched && shortcutKeyEnabledMatched
109681d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                        && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched
109781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                        && isMultiLineMatched && imeActionMatched && localeCodeMatched
109881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                        && languageCodeMatched && countryCodeMatched;
10997a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka
11007a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) {
110181d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                    startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
11027a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_keyboardSetElement),
11037a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "keyboardSetElement"),
11047a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
110505bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_imeAction),
110605bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                                    "imeAction"),
110705bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_navigateNext,
110805bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                                    "navigateNext"),
110905bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_navigatePrevious,
111005bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                                    "navigatePrevious"),
11117a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
11127a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "clobberSettingsKey"),
111305bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_passwordInput,
111405bfd189a88be79ddfc74d0ea21792e2fb78f2aaTadashi G. Takaoka                                    "passwordInput"),
11157a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
11167a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "shortcutKeyEnabled"),
11177a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey,
11187a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "hasShortcutKey"),
111981d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
112081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                                    "languageSwitchKeyEnabled"),
11217a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            booleanAttr(a, R.styleable.Keyboard_Case_isMultiLine,
11227a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "isMultiLine"),
11237a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_localeCode),
11247a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "localeCode"),
11257a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
11267a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "languageCode"),
11277a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            textAttr(a.getString(R.styleable.Keyboard_Case_countryCode),
11287a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                                    "countryCode"),
11297a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                            selected ? "" : " skipped");
11307a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                }
11314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return selected;
11334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
11344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                a.recycle();
11354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchInteger(TypedArray a, int index, int value) {
11394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index) || a.getInt(index, 0) == value;
11424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchBoolean(TypedArray a, int index, boolean value) {
11454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index) || a.getBoolean(index, false) == value;
11484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchString(TypedArray a, int index, String value) {
11514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return !a.hasValue(index)
11544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    || stringArrayContains(a.getString(index).split("\\|"), value);
11554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean matchTypedValue(TypedArray a, int index, int intValue,
11584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                String strValue) {
11594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // If <case> does not have "index" attribute, that means this <case> is wild-card for
11604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // the attribute.
11614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue v = a.peekValue(index);
11624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (v == null)
11634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return true;
11644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isIntegerValue(v)) {
11664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return intValue == a.getInt(index, 0);
11674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else if (isStringValue(v)) {
11684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return stringArrayContains(a.getString(index).split("\\|"), strValue);
11694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return false;
11714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean stringArrayContains(String[] array, String value) {
11744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            for (final String elem : array) {
11754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (elem.equals(value))
11764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    return true;
11774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return false;
11794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private boolean parseDefault(XmlPullParser parser, Row row, boolean skip)
11824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throws XmlPullParserException, IOException {
11837a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            if (DEBUG) startTag("<%s>", TAG_DEFAULT);
11844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (row == null) {
11854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseKeyboardContent(parser, skip);
11864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else {
11874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                parseRowContent(parser, row, skip);
11884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
11894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return true;
11904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
11914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
11924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void parseKeyStyle(XmlPullParser parser, boolean skip)
11937a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                throws XmlPullParserException, IOException {
11944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
11954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_KeyStyle);
11964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
11974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    R.styleable.Keyboard_Key);
11984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            try {
11994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
12004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
12014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                            + "/> needs styleName attribute", parser);
12027a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                if (DEBUG) {
12037a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                    startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE,
12047a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
12057a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                        skip ? " skipped" : "");
12067a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka                }
12074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                if (!skip)
12084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
12094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } finally {
12104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyStyleAttr.recycle();
12114e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                keyAttrs.recycle();
12124e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12137a39bd4454664b5c37b30e9b5362ddbcdce3b374Tadashi G. Takaoka            XmlParseUtils.checkEndTag(TAG_KEY_STYLE, parser);
12144e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12154e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12164e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void startKeyboard() {
12174e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentY += mParams.mTopPadding;
12184e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mTopEdge = true;
12194e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12204e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12214e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void startRow(Row row) {
12224e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
12234e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentRow = row;
12244e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mLeftEdge = true;
12254e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = null;
12264e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12274e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12284e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endRow(Row row) {
12294e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mCurrentRow == null)
12304e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                throw new InflateException("orphant end row tag");
12314e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mRightEdgeKey != null) {
12324e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRightEdgeKey.markAsRightEdge(mParams);
12334e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mRightEdgeKey = null;
12344e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12354e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
12364e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentY += row.mRowHeight;
12374e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mCurrentRow = null;
12384e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mTopEdge = false;
12394e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12404e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12414e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endKey(Key key) {
12424e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mParams.onAddKey(key);
12434e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mLeftEdge) {
12444e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                key.markAsLeftEdge(mParams);
12454e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                mLeftEdge = false;
12464e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12474e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (mTopEdge) {
12484e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                key.markAsTopEdge(mParams);
12494e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12504e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = key;
12514e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12524e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12534e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void endKeyboard() {
12544e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            // nothing to do here.
12554e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12564e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12574e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private void addEdgeSpace(float width, Row row) {
12584e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            row.advanceXPos(width);
12594e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mLeftEdge = false;
12604e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            mRightEdgeKey = null;
12614e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12624e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12634e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static float getDimensionOrFraction(TypedArray a, int index, int base,
12644e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                float defValue) {
12654e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue value = a.peekValue(index);
12664e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (value == null)
12674e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return defValue;
12684e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isFractionValue(value)) {
12694e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getFraction(index, base, base, defValue);
12704e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            } else if (isDimensionValue(value)) {
12714e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getDimension(index, defValue);
12724e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12734e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return defValue;
12744e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12754e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12764e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        public static int getEnumValue(TypedArray a, int index, int defValue) {
12774e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            final TypedValue value = a.peekValue(index);
12784e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (value == null)
12794e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return defValue;
12804e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            if (isIntegerValue(value)) {
12814e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                return a.getInt(index, defValue);
12824e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            }
12834e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return defValue;
12844e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12854e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12864e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isFractionValue(TypedValue v) {
12874e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_FRACTION;
12884e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12894e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12904e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isDimensionValue(TypedValue v) {
12914e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_DIMENSION;
12924e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12934e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12944e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isIntegerValue(TypedValue v) {
12954e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
12964e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
12974e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
12984e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static boolean isStringValue(TypedValue v) {
12994e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return v.type == TypedValue.TYPE_STRING;
13004e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
13014e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
13024e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static String textAttr(String value, String name) {
13034e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return value != null ? String.format(" %s=%s", name, value) : "";
13044e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
13054e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka
13064e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        private static String booleanAttr(TypedArray a, int index, String name) {
13074e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka            return a.hasValue(index)
13084e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka                    ? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
13094e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka        }
13104e1dab8cfaad891fe041ed8d71893186c05cef71Tadashi G. Takaoka    }
13115a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka}
1312