1803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka/* 2803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * Copyright (C) 2013 The Android Open Source Project 3803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * 4803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 5803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * you may not use this file except in compliance with the License. 6803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * You may obtain a copy of the License at 7803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * 8803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 9803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * 10803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software 11803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 12803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * See the License for the specific language governing permissions and 14803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka * limitations under the License. 15803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka */ 16803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 179f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaokapackage com.android.inputmethod.keyboard.emoji; 18803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 19f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataokaimport android.content.SharedPreferences; 20803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaokaimport android.text.TextUtils; 21ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataokaimport android.util.Log; 22803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 23803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Key; 24803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard; 25f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataokaimport com.android.inputmethod.latin.settings.Settings; 262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasaimport com.android.inputmethod.latin.utils.JsonUtils; 27803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 28803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaokaimport java.util.ArrayDeque; 29ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataokaimport java.util.ArrayList; 30f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataokaimport java.util.Collection; 315326dcfb7dbdc1a3fc9cfb94046805f18bf3d3d7Tadashi G. Takaokaimport java.util.Collections; 32ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataokaimport java.util.List; 33803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 34803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka/** 352c011c697b70bb8c1ce519b1d38f099ad67a5c5eSatoshi Kataoka * This is a Keyboard class where you can add keys dynamically shown in a grid layout 36803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka */ 379f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaokafinal class DynamicGridKeyboard extends Keyboard { 38ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka private static final String TAG = DynamicGridKeyboard.class.getSimpleName(); 39803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka private static final int TEMPLATE_KEY_CODE_0 = 0x30; 40803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka private static final int TEMPLATE_KEY_CODE_1 = 0x31; 41e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka private final Object mLock = new Object(); 42803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 43f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka private final SharedPreferences mPrefs; 44803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka private final int mHorizontalStep; 45803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka private final int mVerticalStep; 46803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka private final int mColumnsNum; 47b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka private final int mMaxKeyCount; 48f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka private final boolean mIsRecents; 49a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka private final ArrayDeque<GridKey> mGridKeys = new ArrayDeque<>(); 50a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka private final ArrayDeque<Key> mPendingKeys = new ArrayDeque<>(); 51803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 525326dcfb7dbdc1a3fc9cfb94046805f18bf3d3d7Tadashi G. Takaoka private List<Key> mCachedGridKeys; 53803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 54f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard, 552fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa final int maxKeyCount, final int categoryId) { 56803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka super(templateKeyboard); 57803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0); 58803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1); 59803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka mHorizontalStep = Math.abs(key1.getX() - key0.getX()); 60803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka mVerticalStep = key0.getHeight() + mVerticalGap; 61803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka mColumnsNum = mBaseWidth / mHorizontalStep; 625dfbc8af2439152ca6b6759309aced5bb879b518Satoshi Kataoka mMaxKeyCount = maxKeyCount; 63ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka mIsRecents = categoryId == EmojiCategory.ID_RECENTS; 64f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka mPrefs = prefs; 65803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 66803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 67803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka private Key getTemplateKey(final int code) { 68c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka for (final Key key : super.getSortedKeys()) { 69803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka if (key.getCode() == code) { 70803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka return key; 71803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 72803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 73803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka throw new RuntimeException("Can't find template key: code=" + code); 74803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 75803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 76e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka public void addPendingKey(final Key usedKey) { 77e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka synchronized (mLock) { 78e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka mPendingKeys.addLast(usedKey); 79e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka } 80e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka } 81e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka 82e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka public void flushPendingRecentKeys() { 83e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka synchronized (mLock) { 84e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka while (!mPendingKeys.isEmpty()) { 85e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka addKey(mPendingKeys.pollFirst(), true); 86e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka } 87e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka saveRecentKeys(); 88e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka } 89e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka } 90e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka 91b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka public void addKeyFirst(final Key usedKey) { 92b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka addKey(usedKey, true); 93f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka if (mIsRecents) { 94f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka saveRecentKeys(); 95f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka } 96b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka } 97b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka 98b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka public void addKeyLast(final Key usedKey) { 99b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka addKey(usedKey, false); 100b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka } 101803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 102b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka private void addKey(final Key usedKey, final boolean addFirst) { 103ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka if (usedKey == null) { 104ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka return; 105ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka } 106e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka synchronized (mLock) { 107b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka mCachedGridKeys = null; 108b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka final GridKey key = new GridKey(usedKey); 109b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka while (mGridKeys.remove(key)) { 110803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka // Remove duplicate keys. 111803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 112b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka if (addFirst) { 113b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka mGridKeys.addFirst(key); 114b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka } else { 115b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka mGridKeys.addLast(key); 116b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka } 117b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka while (mGridKeys.size() > mMaxKeyCount) { 118b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka mGridKeys.removeLast(); 119803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 120803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka int index = 0; 121b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka for (final GridKey gridKey : mGridKeys) { 1225d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka final int keyX0 = getKeyX0(index); 1235d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka final int keyY0 = getKeyY0(index); 1245d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka final int keyX1 = getKeyX1(index); 1255d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka final int keyY1 = getKeyY1(index); 1262fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa gridKey.updateCoordinates(keyX0, keyY0, keyX1, keyY1); 127803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka index++; 128803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 129803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 130803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 131803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 132f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka private void saveRecentKeys() { 133a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka final ArrayList<Object> keys = new ArrayList<>(); 134f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka for (final Key key : mGridKeys) { 135ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka if (key.getOutputText() != null) { 136ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka keys.add(key.getOutputText()); 137ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka } else { 138ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka keys.add(key.getCode()); 139ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka } 140f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka } 1412fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa final String jsonStr = JsonUtils.listToJsonStr(keys); 142ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka Settings.writeEmojiRecentKeys(mPrefs, jsonStr); 143f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka } 144f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka 1452fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa private static Key getKeyByCode(final Collection<DynamicGridKeyboard> keyboards, 1462fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa final int code) { 1472fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa for (final DynamicGridKeyboard keyboard : keyboards) { 1480ea82be889df77546956c0fe93664622fa4ccb29Tadashi G. Takaoka for (final Key key : keyboard.getSortedKeys()) { 1490ea82be889df77546956c0fe93664622fa4ccb29Tadashi G. Takaoka if (key.getCode() == code) { 1500ea82be889df77546956c0fe93664622fa4ccb29Tadashi G. Takaoka return key; 1510ea82be889df77546956c0fe93664622fa4ccb29Tadashi G. Takaoka } 1522fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 1532fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 1542fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa return null; 1552fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 1562fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa 1572fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa private static Key getKeyByOutputText(final Collection<DynamicGridKeyboard> keyboards, 1582fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa final String outputText) { 1590ea82be889df77546956c0fe93664622fa4ccb29Tadashi G. Takaoka for (final DynamicGridKeyboard keyboard : keyboards) { 1600ea82be889df77546956c0fe93664622fa4ccb29Tadashi G. Takaoka for (final Key key : keyboard.getSortedKeys()) { 1610ea82be889df77546956c0fe93664622fa4ccb29Tadashi G. Takaoka if (outputText.equals(key.getOutputText())) { 1620ea82be889df77546956c0fe93664622fa4ccb29Tadashi G. Takaoka return key; 1630ea82be889df77546956c0fe93664622fa4ccb29Tadashi G. Takaoka } 164f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka } 165f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka } 166ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka return null; 167ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka } 168ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka 1692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa public void loadRecentKeys(final Collection<DynamicGridKeyboard> keyboards) { 170ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka final String str = Settings.readEmojiRecentKeys(mPrefs); 1712fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa final List<Object> keys = JsonUtils.jsonStrToList(str); 172ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka for (final Object o : keys) { 1732fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa final Key key; 1742fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa if (o instanceof Integer) { 1752fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa final int code = (Integer)o; 1762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa key = getKeyByCode(keyboards, code); 1772fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } else if (o instanceof String) { 1782fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa final String outputText = (String)o; 1792fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa key = getKeyByOutputText(keyboards, outputText); 1802fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } else { 1812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa Log.w(TAG, "Invalid object: " + o); 1822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa continue; 1832fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa } 1842fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa addKeyLast(key); 185ea7bfa5cd58c4ce0fed5d79c3118d5339ef4e66aSatoshi Kataoka } 186f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka } 187f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka 1885d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka private int getKeyX0(final int index) { 189803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka final int column = index % mColumnsNum; 1905d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka return column * mHorizontalStep; 191803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 192803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 1935d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka private int getKeyX1(final int index) { 1945d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka final int column = index % mColumnsNum + 1; 1955d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka return column * mHorizontalStep; 1965d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka } 1975d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka 1985d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka private int getKeyY0(final int index) { 199803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka final int row = index / mColumnsNum; 20030ef03d865ec78469f26983f9c3e74f4e2c1bdd0Satoshi Kataoka return row * mVerticalStep + mVerticalGap / 2; 2015d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka } 2025d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka 2035d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka private int getKeyY1(final int index) { 2045d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka final int row = index / mColumnsNum + 1; 20530ef03d865ec78469f26983f9c3e74f4e2c1bdd0Satoshi Kataoka return row * mVerticalStep + mVerticalGap / 2; 206803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 207803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 208803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka @Override 209c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka public List<Key> getSortedKeys() { 210e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka synchronized (mLock) { 211b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka if (mCachedGridKeys != null) { 212b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka return mCachedGridKeys; 213803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 214965621574dc102a1578636f641e5eb8717fb3601Tadashi G. Takaoka final ArrayList<Key> cachedKeys = new ArrayList<Key>(mGridKeys); 2155326dcfb7dbdc1a3fc9cfb94046805f18bf3d3d7Tadashi G. Takaoka mCachedGridKeys = Collections.unmodifiableList(cachedKeys); 216b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka return mCachedGridKeys; 217803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 218803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 219803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 220803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka @Override 22158d4e610ac705fbfb49d8ec8d893a35ac416668eTadashi G. Takaoka public List<Key> getNearestKeys(final int x, final int y) { 222b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka // TODO: Calculate the nearest key index in mGridKeys from x and y. 223c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka return getSortedKeys(); 224803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 225803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 226b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka static final class GridKey extends Key { 227803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka private int mCurrentX; 228803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka private int mCurrentY; 229803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 230b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka public GridKey(final Key originalKey) { 231803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka super(originalKey); 232803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 233803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 2342fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa public void updateCoordinates(final int x0, final int y0, final int x1, final int y1) { 2355d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka mCurrentX = x0; 2365d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka mCurrentY = y0; 2375d27cb939771b1bacf37843c440bb9488eaa8797Satoshi Kataoka getHitBox().set(x0, y0, x1, y1); 238803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 239803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 240803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka @Override 241803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka public int getX() { 242803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka return mCurrentX; 243803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 244803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 245803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka @Override 246803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka public int getY() { 247803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka return mCurrentY; 248803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 249803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 250803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka @Override 251803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka public boolean equals(final Object o) { 252803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka if (!(o instanceof Key)) return false; 253803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka final Key key = (Key)o; 254803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka if (getCode() != key.getCode()) return false; 255803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka if (!TextUtils.equals(getLabel(), key.getLabel())) return false; 256803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka return TextUtils.equals(getOutputText(), key.getOutputText()); 257803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 258803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka 259803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka @Override 260803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka public String toString() { 261b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka return "GridKey: " + super.toString(); 262803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 263803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka } 264803ea61d552e9c1df34f5d58f1e59e5b7612d6e1Tadashi G. Takaoka} 265