15a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka/*
28632bff2d5a8e1160989008dea6eff4b94b065ddTadashi G. Takaoka * Copyright (C) 2010 The Android Open Source Project
35a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *
48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at
75a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *
88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi 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
118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and
148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License.
155a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka */
165a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
175a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
185a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1956853c1e48deb71367104060b49b7b1f0344a476Tadashi G. Takaokaimport android.util.SparseArray;
205a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
21dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
22c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
2335ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardParams;
24240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaokaimport com.android.inputmethod.latin.Constants;
251d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalardimport com.android.inputmethod.latin.utils.CoordinateUtils;
26b7758d6f912093747d4b18fbc8d1dcd77c7d1f9bTadashi G. Takaoka
27a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaokaimport java.util.ArrayList;
28064af5c0fc07b3afae3021d42fcac2185158c951Tadashi G. Takaokaimport java.util.Collections;
29064af5c0fc07b3afae3021d42fcac2185158c951Tadashi G. Takaokaimport java.util.List;
30064af5c0fc07b3afae3021d42fcac2185158c951Tadashi G. Takaoka
315a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka/**
325a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
335a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * consists of rows of keys.
345a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
355a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * <pre>
365a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * &lt;Keyboard
375ee2d79e41872610946b5a5c1caf14f3e5696c26Tadashi G. Takaoka *         latin:keyWidth="10%p"
385ee2d79e41872610946b5a5c1caf14f3e5696c26Tadashi G. Takaoka *         latin:rowHeight="50px"
395ee2d79e41872610946b5a5c1caf14f3e5696c26Tadashi G. Takaoka *         latin:horizontalGap="2%p"
405ee2d79e41872610946b5a5c1caf14f3e5696c26Tadashi G. Takaoka *         latin:verticalGap="2%p" &gt;
415ee2d79e41872610946b5a5c1caf14f3e5696c26Tadashi G. Takaoka *     &lt;Row latin:keyWidth="10%p" &gt;
425a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         &lt;Key latin:keyLabel="A" /&gt;
435a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *         ...
445a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *     &lt;/Row&gt;
455a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka *     ...
465a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * &lt;/Keyboard&gt;
475a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * </pre>
485a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka */
495a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapublic class Keyboard {
50167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka    public final KeyboardId mId;
5163584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka    public final int mThemeId;
52167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka
53167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka    /** Total height of the keyboard, including the padding and keys */
548da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mOccupiedHeight;
558da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    /** Total width of the keyboard, including the padding and keys */
568da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mOccupiedWidth;
57167e77f17084da5c223a3a790d3dd3d749e68ae3Tadashi G. Takaoka
582f5a933c04252257bc2ec747948017da40d4a484Tadashi G. Takaoka    /** Base height of the keyboard, used to calculate rows' height */
592f5a933c04252257bc2ec747948017da40d4a484Tadashi G. Takaoka    public final int mBaseHeight;
602f5a933c04252257bc2ec747948017da40d4a484Tadashi G. Takaoka    /** Base width of the keyboard, used to calculate keys' width */
612f5a933c04252257bc2ec747948017da40d4a484Tadashi G. Takaoka    public final int mBaseWidth;
622f5a933c04252257bc2ec747948017da40d4a484Tadashi G. Takaoka
638fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    /** The padding above the keyboard */
648fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    public final int mTopPadding;
655a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    /** Default gap between rows */
668da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mVerticalGap;
678da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
68d20652c878aae2e0e46a33310672a2786ee55b0cTadashi G. Takaoka    /** Per keyboard key visual parameters */
69dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public final KeyVisualAttributes mKeyVisualAttributes;
70d20652c878aae2e0e46a33310672a2786ee55b0cTadashi G. Takaoka
718fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka    public final int mMostCommonKeyHeight;
728da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final int mMostCommonKeyWidth;
735a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
749d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    /** More keys keyboard template */
759d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public final int mMoreKeysTemplate;
769b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka
772affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    /** Maximum column for more keys keyboard */
782affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    public final int mMaxMoreKeysKeyboardColumn;
799b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka
805326dcfb7dbdc1a3fc9cfb94046805f18bf3d3d7Tadashi G. Takaoka    /** List of keys in this keyboard */
81c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka    private final List<Key> mSortedKeys;
82064af5c0fc07b3afae3021d42fcac2185158c951Tadashi G. Takaoka    public final List<Key> mShiftKeys;
83064af5c0fc07b3afae3021d42fcac2185158c951Tadashi G. Takaoka    public final List<Key> mAltCodeKeysWhileTyping;
848da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka    public final KeyboardIconsSet mIconsSet;
856fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka
86a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka    private final SparseArray<Key> mKeyCache = new SparseArray<>();
87c2a21786e526cc32e48a577a55b1b7e72ae1a6ddTadashi G. Takaoka
888fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    private final ProximityInfo mProximityInfo;
898dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka    private final boolean mProximityCharsCorrectionEnabled;
908fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
9135ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka    public Keyboard(final KeyboardParams params) {
928da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mId = params.mId;
9363584323cab56c76debf6bb000621f2c605329a9Tadashi G. Takaoka        mThemeId = params.mThemeId;
948da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mOccupiedHeight = params.mOccupiedHeight;
958da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mOccupiedWidth = params.mOccupiedWidth;
962f5a933c04252257bc2ec747948017da40d4a484Tadashi G. Takaoka        mBaseHeight = params.mBaseHeight;
972f5a933c04252257bc2ec747948017da40d4a484Tadashi G. Takaoka        mBaseWidth = params.mBaseWidth;
988fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka        mMostCommonKeyHeight = params.mMostCommonKeyHeight;
998da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mMostCommonKeyWidth = params.mMostCommonKeyWidth;
1009d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysTemplate = params.mMoreKeysTemplate;
1012affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn;
102dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        mKeyVisualAttributes = params.mKeyVisualAttributes;
1038fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka        mTopPadding = params.mTopPadding;
1048da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mVerticalGap = params.mVerticalGap;
1058da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
106a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka        mSortedKeys = Collections.unmodifiableList(new ArrayList<>(params.mSortedKeys));
107064af5c0fc07b3afae3021d42fcac2185158c951Tadashi G. Takaoka        mShiftKeys = Collections.unmodifiableList(params.mShiftKeys);
108064af5c0fc07b3afae3021d42fcac2185158c951Tadashi G. Takaoka        mAltCodeKeysWhileTyping = Collections.unmodifiableList(params.mAltCodeKeysWhileTyping);
1098da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka        mIconsSet = params.mIconsSet;
1105a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
1117ef1dabd92a9dae042965cd10d08a2cd47455dccsatok        mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(),
1128da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
113c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka                mMostCommonKeyWidth, mMostCommonKeyHeight, mSortedKeys,
114c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka                params.mTouchPositionCorrection);
1158dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka        mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
1168dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka    }
1178dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka
1180b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka    protected Keyboard(final Keyboard keyboard) {
1190b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mId = keyboard.mId;
1200b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mThemeId = keyboard.mThemeId;
1210b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mOccupiedHeight = keyboard.mOccupiedHeight;
1220b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mOccupiedWidth = keyboard.mOccupiedWidth;
1232f5a933c04252257bc2ec747948017da40d4a484Tadashi G. Takaoka        mBaseHeight = keyboard.mBaseHeight;
1242f5a933c04252257bc2ec747948017da40d4a484Tadashi G. Takaoka        mBaseWidth = keyboard.mBaseWidth;
1250b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mMostCommonKeyHeight = keyboard.mMostCommonKeyHeight;
1260b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mMostCommonKeyWidth = keyboard.mMostCommonKeyWidth;
1270b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mMoreKeysTemplate = keyboard.mMoreKeysTemplate;
1280b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mMaxMoreKeysKeyboardColumn = keyboard.mMaxMoreKeysKeyboardColumn;
1290b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mKeyVisualAttributes = keyboard.mKeyVisualAttributes;
1300b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mTopPadding = keyboard.mTopPadding;
1310b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mVerticalGap = keyboard.mVerticalGap;
1320b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka
133c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka        mSortedKeys = keyboard.mSortedKeys;
1340b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mShiftKeys = keyboard.mShiftKeys;
1350b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mAltCodeKeysWhileTyping = keyboard.mAltCodeKeysWhileTyping;
1360b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mIconsSet = keyboard.mIconsSet;
1370b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka
1380b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mProximityInfo = keyboard.mProximityInfo;
1390b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka        mProximityCharsCorrectionEnabled = keyboard.mProximityCharsCorrectionEnabled;
1400b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka    }
1410b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka
14235ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka    public boolean hasProximityCharsCorrection(final int code) {
1438dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka        if (!mProximityCharsCorrectionEnabled) {
1448dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka            return false;
1458dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka        }
1468dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka        // Note: The native code has the main keyboard layout only at this moment.
1478dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka        // TODO: Figure out how to handle proximity characters information of all layouts.
1488dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka        final boolean canAssumeNativeHasProximityCharsInfoOfAllKeys = (
1498dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                mId.mElementId == KeyboardId.ELEMENT_ALPHABET
1508dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka                || mId.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED);
1518dcad2ed4776b849ddacb623a94b4301afcf055aTadashi G. Takaoka        return canAssumeNativeHasProximityCharsInfoOfAllKeys || Character.isLetter(code);
1528fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1538fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
154043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard    public ProximityInfo getProximityInfo() {
155043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard        return mProximityInfo;
1565a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
1575a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
158c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka    /**
159c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka     * Return the sorted list of keys of this keyboard.
160c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka     * The keys are sorted from top-left to bottom-right order.
161a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka     * The list may contain {@link Key.Spacer} object as well.
162c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka     * @return the sorted unmodifiable list of {@link Key}s of this keyboard.
163c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka     */
164c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka    public List<Key> getSortedKeys() {
165c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka        return mSortedKeys;
1660b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka    }
1670b3d5e12e38291e7f06a1a5cdf157af61217465fTadashi G. Takaoka
16835ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka    public Key getKey(final int code) {
169240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (code == Constants.CODE_UNSPECIFIED) {
170623d0155b6a316fdc9335370cdd4005bbb474ef3Tadashi G. Takaoka            return null;
171623d0155b6a316fdc9335370cdd4005bbb474ef3Tadashi G. Takaoka        }
1728f6c603b3bc9b1f81a0ab2299429d725b463b92fTadashi G. Takaoka        synchronized (mKeyCache) {
1738f6c603b3bc9b1f81a0ab2299429d725b463b92fTadashi G. Takaoka            final int index = mKeyCache.indexOfKey(code);
1748f6c603b3bc9b1f81a0ab2299429d725b463b92fTadashi G. Takaoka            if (index >= 0) {
1758f6c603b3bc9b1f81a0ab2299429d725b463b92fTadashi G. Takaoka                return mKeyCache.valueAt(index);
1768f6c603b3bc9b1f81a0ab2299429d725b463b92fTadashi G. Takaoka            }
1772013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
178c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka            for (final Key key : getSortedKeys()) {
1797dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                if (key.getCode() == code) {
1808f6c603b3bc9b1f81a0ab2299429d725b463b92fTadashi G. Takaoka                    mKeyCache.put(code, key);
1818f6c603b3bc9b1f81a0ab2299429d725b463b92fTadashi G. Takaoka                    return key;
1828f6c603b3bc9b1f81a0ab2299429d725b463b92fTadashi G. Takaoka                }
1832013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka            }
1848f6c603b3bc9b1f81a0ab2299429d725b463b92fTadashi G. Takaoka            mKeyCache.put(code, null);
1858f6c603b3bc9b1f81a0ab2299429d725b463b92fTadashi G. Takaoka            return null;
1862013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka        }
1872013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka    }
1882013bab89ca2f82589f99d98d9cf3b41ea5aac65Tadashi G. Takaoka
18935ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka    public boolean hasKey(final Key aKey) {
19056853c1e48deb71367104060b49b7b1f0344a476Tadashi G. Takaoka        if (mKeyCache.indexOfValue(aKey) >= 0) {
191b7a5a6820038824568dc5251ae5080b34a4933b8Tadashi G. Takaoka            return true;
192b7a5a6820038824568dc5251ae5080b34a4933b8Tadashi G. Takaoka        }
193b7a5a6820038824568dc5251ae5080b34a4933b8Tadashi G. Takaoka
194c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka        for (final Key key : getSortedKeys()) {
195b7a5a6820038824568dc5251ae5080b34a4933b8Tadashi G. Takaoka            if (key == aKey) {
1967dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka                mKeyCache.put(key.getCode(), key);
197b7a5a6820038824568dc5251ae5080b34a4933b8Tadashi G. Takaoka                return true;
198b7a5a6820038824568dc5251ae5080b34a4933b8Tadashi G. Takaoka            }
199b7a5a6820038824568dc5251ae5080b34a4933b8Tadashi G. Takaoka        }
200b7a5a6820038824568dc5251ae5080b34a4933b8Tadashi G. Takaoka        return false;
201b7a5a6820038824568dc5251ae5080b34a4933b8Tadashi G. Takaoka    }
202b7a5a6820038824568dc5251ae5080b34a4933b8Tadashi G. Takaoka
20335336cf83a434514c60d79389ceb818cba642506Tadashi G. Takaoka    @Override
20435336cf83a434514c60d79389ceb818cba642506Tadashi G. Takaoka    public String toString() {
20535336cf83a434514c60d79389ceb818cba642506Tadashi G. Takaoka        return mId.toString();
20635336cf83a434514c60d79389ceb818cba642506Tadashi G. Takaoka    }
20735336cf83a434514c60d79389ceb818cba642506Tadashi G. Takaoka
2085a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    /**
2090c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka     * Returns the array of the keys that are closest to the given point.
2105a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * @param x the x-coordinate of the point
2115a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * @param y the y-coordinate of the point
21258d4e610ac705fbfb49d8ec8d893a35ac416668eTadashi G. Takaoka     * @return the list of the nearest keys to the given point. If the given
2135a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     * point is out of range, then an array of size zero is returned.
2145a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka     */
21558d4e610ac705fbfb49d8ec8d893a35ac416668eTadashi G. Takaoka    public List<Key> getNearestKeys(final int x, final int y) {
2164f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        // Avoid dead pixels at edges of the keyboard
2174f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1));
2184f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
2194f7d278af62a89ef3f45cc9ebbfb076a5a352c76Ken Wakasa        return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
2205a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka    }
2211d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard
2221d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard    public int[] getCoordinates(final int[] codePoints) {
2231d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard        final int length = codePoints.length;
2241d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard        final int[] coordinates = CoordinateUtils.newCoordinateArray(length);
2251d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard        for (int i = 0; i < length; ++i) {
2261d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard            final Key key = getKey(codePoints[i]);
2271d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard            if (null != key) {
2281d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard                CoordinateUtils.setXYInArray(coordinates, i,
2291d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard                        key.getX() + key.getWidth() / 2, key.getY() + key.getHeight() / 2);
2301d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard            } else {
2311d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard                CoordinateUtils.setXYInArray(coordinates, i,
2321d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard                        Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
2331d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard            }
2341d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard        }
2351d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard        return coordinates;
2361d6e647ac31eab38f30b8ed468551d1e3a07c705Jean Chalard    }
2375a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka}
238