MoreKeysKeyboard.java revision aeeed758480b0fac848f4556884d978f3004555b
104c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka/* 28632bff2d5a8e1160989008dea6eff4b94b065ddTadashi G. Takaoka * Copyright (C) 2011 The Android Open Source Project 304c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * 404c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); you may not 504c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * use this file except in compliance with the License. You may obtain a copy of 604c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * the License at 704c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * 804c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 904c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * 1004c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software 1104c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 1204c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 1304c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * License for the specific language governing permissions and limitations under 1404c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka * the License. 1504c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka */ 1604c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka 1704c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaokapackage com.android.inputmethod.keyboard; 1804c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka 1932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaokaimport android.graphics.Paint; 2032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 21e01d272603f3643ce613e61dd3204379f4f4fb73Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeySpecParser; 2232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaokaimport com.android.inputmethod.latin.R; 2304c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka 242affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaokapublic class MoreKeysKeyboard extends Keyboard { 258da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka private final int mDefaultKeyCoordX; 2604c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka 276213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka MoreKeysKeyboard(Builder.MoreKeysKeyboardParams params) { 288da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka super(params); 298da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2; 3004c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka } 3104c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka 3204c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka public int getDefaultCoordX() { 3304c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka return mDefaultKeyCoordX; 3404c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka } 3532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 362affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka public static class Builder extends Keyboard.Builder<Builder.MoreKeysKeyboardParams> { 37bd7b160cfb05ee543e3cb6ddc7bd231b3f3aba0bTadashi G. Takaoka private final String[] mMoreKeys; 3832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 392affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka public static class MoreKeysKeyboardParams extends Keyboard.Params { 40aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka public boolean mIsFixedOrder; 4132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka /* package */int mTopRowAdjustment; 4232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka public int mNumRows; 4332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka public int mNumColumns; 44aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka public int mTopKeys; 4532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka public int mLeftKeys; 4632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka public int mRightKeys; // includes default key. 4732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 482affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka public MoreKeysKeyboardParams() { 4932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka super(); 5032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 5132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 522affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka /* package for test */MoreKeysKeyboardParams(int numKeys, int maxColumns, int keyWidth, 5332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka int rowHeight, int coordXInParent, int parentKeyboardWidth) { 5432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka super(); 5532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka setParameters(numKeys, maxColumns, keyWidth, rowHeight, coordXInParent, 5632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka parentKeyboardWidth); 5732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 5832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 5932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka /** 602affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka * Set keyboard parameters of more keys keyboard. 6132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka * 622affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka * @param numKeys number of keys in this more keys keyboard. 63aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka * @param maxColumnsAndFlags number of maximum columns of this more keys keyboard. 64aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka * This might have {@link Key#MORE_KEYS_FIXED_COLUMN_ORDER} flag. 652affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka * @param keyWidth more keys keyboard key width in pixel, including horizontal gap. 662affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka * @param rowHeight more keys keyboard row height in pixel, including vertical gap. 672affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka * @param coordXInParent coordinate x of the key preview in parent keyboard. 6832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka * @param parentKeyboardWidth parent keyboard width in pixel. 6932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka */ 70aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka public void setParameters(int numKeys, int maxColumnsAndFlags, int keyWidth, 71aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka int rowHeight, int coordXInParent, int parentKeyboardWidth) { 72aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka mIsFixedOrder = (maxColumnsAndFlags & Key.MORE_KEYS_FIXED_COLUMN_ORDER) != 0; 73aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int maxColumns = maxColumnsAndFlags & ~Key.MORE_KEYS_FIXED_COLUMN_ORDER; 7432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (parentKeyboardWidth / keyWidth < maxColumns) { 7532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka throw new IllegalArgumentException( 762affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka "Keyboard is too small to hold more keys keyboard: " 772affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka + parentKeyboardWidth + " " + keyWidth + " " + maxColumns); 7832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 7932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka mDefaultKeyWidth = keyWidth; 8032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka mDefaultRowHeight = rowHeight; 8132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 8232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka final int numRows = (numKeys + maxColumns - 1) / maxColumns; 8332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka mNumRows = numRows; 84aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int numColumns = mIsFixedOrder ? Math.min(numKeys, maxColumns) 85aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka : getOptimizedColumns(numKeys, maxColumns); 8632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka mNumColumns = numColumns; 87aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int topKeys = numKeys % numColumns; 88aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka mTopKeys = topKeys == 0 ? numColumns : topKeys; 8932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 9032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka final int numLeftKeys = (numColumns - 1) / 2; 9132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka final int numRightKeys = numColumns - numLeftKeys; // including default key. 926213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka // Maximum number of keys we can layout both side of the parent key 9332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka final int maxLeftKeys = coordXInParent / keyWidth; 946213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka final int maxRightKeys = (parentKeyboardWidth - coordXInParent) / keyWidth; 9532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka int leftKeys, rightKeys; 9632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (numLeftKeys > maxLeftKeys) { 9732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka leftKeys = maxLeftKeys; 986213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka rightKeys = numColumns - leftKeys; 996213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka } else if (numRightKeys > maxRightKeys + 1) { 1006213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka rightKeys = maxRightKeys + 1; // include default key 1016213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka leftKeys = numColumns - rightKeys; 10232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } else { 10332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka leftKeys = numLeftKeys; 10432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka rightKeys = numRightKeys; 10532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 1066213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka // If the left keys fill the left side of the parent key, entire more keys keyboard 1076213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka // should be shifted to the right unless the parent key is on the left edge. 1086213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka if (maxLeftKeys == leftKeys && leftKeys > 0) { 10932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka leftKeys--; 11032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka rightKeys++; 11132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 1126213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka // If the right keys fill the right side of the parent key, entire more keys 1136213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka // should be shifted to the left unless the parent key is on the right edge. 1146213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka if (maxRightKeys == rightKeys - 1 && rightKeys > 1) { 11532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka leftKeys++; 11632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka rightKeys--; 11732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 11832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka mLeftKeys = leftKeys; 11932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka mRightKeys = rightKeys; 12032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 121aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka // Adjustment of the top row. 122aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka mTopRowAdjustment = mIsFixedOrder ? getFixedOrderTopRowAdjustment() 123aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka : getAutoOrderTopRowAdjustment(); 1248fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka mBaseWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth; 125ea0c567f86bd19015d53fc038c4579df776cfec3Tadashi G. Takaoka // Need to subtract the bottom row's gutter only. 1268fbfac4ffb7079e8e71fd4e3ddc04e362239ebb3Tadashi G. Takaoka mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap 127ea0c567f86bd19015d53fc038c4579df776cfec3Tadashi G. Takaoka + mTopPadding + mBottomPadding; 12832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 12932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 130aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka private int getFixedOrderTopRowAdjustment() { 131aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka if (mNumRows == 1 || mTopKeys % 2 == 1 || mTopKeys == mNumColumns 132aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka || mLeftKeys == 0 || mRightKeys == 1) { 133aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return 0; 134aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } 135aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return -1; 136aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } 137aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka 138aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka private int getAutoOrderTopRowAdjustment() { 139aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka if (mNumRows == 1 || mTopKeys == 1 || mNumColumns % 2 == mTopKeys % 2 140aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka || mLeftKeys == 0 || mRightKeys == 1) { 141aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return 0; 142aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } 143aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return -1; 144aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } 145aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka 14632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka // Return key position according to column count (0 is default). 14732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka /* package */int getColumnPos(int n) { 148aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return mIsFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n); 149aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } 150aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka 151aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka private int getFixedOrderColumnPos(int n) { 152aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int col = n % mNumColumns; 153aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int row = n / mNumColumns; 154aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka if (!isTopRow(row)) { 155aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return col - mLeftKeys; 156aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } 157aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int rightSideKeys = mTopKeys / 2; 158aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int leftSideKeys = mTopKeys - (rightSideKeys + 1); 159aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int pos = col - leftSideKeys; 160aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int numLeftKeys = mLeftKeys + mTopRowAdjustment; 161aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int numRightKeys = mRightKeys - 1; 162aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka if (numRightKeys >= rightSideKeys && numLeftKeys >= leftSideKeys) { 163aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return pos; 164aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } else if (numRightKeys < rightSideKeys) { 165aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return pos - (rightSideKeys - numRightKeys); 166aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } else { // numLeftKeys < leftSideKeys 167aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return pos + (leftSideKeys - numLeftKeys); 168aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } 169aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } 170aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka 171aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka private int getAutomaticColumnPos(int n) { 17232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka final int col = n % mNumColumns; 173aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int row = n / mNumColumns; 174aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka int leftKeys = mLeftKeys; 175aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka if (isTopRow(row)) { 176aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka leftKeys += mTopRowAdjustment; 177aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka } 17832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (col == 0) { 17932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka // default position. 18032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka return 0; 18132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 182aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka 18332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka int pos = 0; 18432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka int right = 1; // include default position key. 18532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka int left = 0; 18632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka int i = 0; 18732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka while (true) { 18832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka // Assign right key if available. 18932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (right < mRightKeys) { 19032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka pos = right; 19132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka right++; 19232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka i++; 19332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 19432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (i >= col) 19532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka break; 19632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka // Assign left key if available. 197aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka if (left < leftKeys) { 19832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka left++; 19932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka pos = -left; 20032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka i++; 20132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 20232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (i >= col) 20332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka break; 20432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 20532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka return pos; 20632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 20732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 20832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka private static int getTopRowEmptySlots(int numKeys, int numColumns) { 209aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka final int remainings = numKeys % numColumns; 210aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return remainings == 0 ? 0 : numColumns - remainings; 21132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 21232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 21332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka private int getOptimizedColumns(int numKeys, int maxColumns) { 21432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka int numColumns = Math.min(numKeys, maxColumns); 21532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) { 21632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka numColumns--; 21732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 21832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka return numColumns; 21932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 22032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 22132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka public int getDefaultKeyCoordX() { 22232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka return mLeftKeys * mDefaultKeyWidth; 22332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 22432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 22532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka public int getX(int n, int row) { 22632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka final int x = getColumnPos(n) * mDefaultKeyWidth + getDefaultKeyCoordX(); 22732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (isTopRow(row)) { 22832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka return x + mTopRowAdjustment * (mDefaultKeyWidth / 2); 22932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 23032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka return x; 23132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 23232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 23332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka public int getY(int row) { 23432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding; 23532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 23632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 2372fc4248700023853980b0006c12425079e3f9257Tadashi G. Takaoka public void markAsEdgeKey(Key key, int row) { 23832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (row == 0) 2392fc4248700023853980b0006c12425079e3f9257Tadashi G. Takaoka key.markAsTopEdge(this); 24032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (isTopRow(row)) 2412fc4248700023853980b0006c12425079e3f9257Tadashi G. Takaoka key.markAsBottomEdge(this); 24232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 24332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 24432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka private boolean isTopRow(int rowCount) { 245aeeed758480b0fac848f4556884d978f3004555bTadashi G. Takaoka return mNumRows > 1 && rowCount == mNumRows - 1; 24632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 24732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 24832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 24932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka public Builder(KeyboardView view, int xmlId, Key parentKey, Keyboard parentKeyboard) { 2502affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka super(view.getContext(), new MoreKeysKeyboardParams()); 251645128af712961456a42cbcc34c0cdf5f0b40a83Tadashi G. Takaoka load(xmlId, parentKeyboard.mId); 25232572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 2532affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka // TODO: More keys keyboard's vertical gap is currently calculated heuristically. 254ea0c567f86bd19015d53fc038c4579df776cfec3Tadashi G. Takaoka // Should revise the algorithm. 255ea0c567f86bd19015d53fc038c4579df776cfec3Tadashi G. Takaoka mParams.mVerticalGap = parentKeyboard.mVerticalGap / 2; 2569d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeys = parentKey.mMoreKeys; 25732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 258a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka final int previewWidth = view.mKeyPreviewDrawParams.mPreviewBackgroundWidth; 259a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka final int previewHeight = view.mKeyPreviewDrawParams.mPreviewBackgroundHeight; 260a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka final int width, height; 2612affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka // Use pre-computed width and height if these values are available and more keys 2622affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka // keyboard has only one key to mitigate visual flicker between key preview and more 2632affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka // keys keyboard. 264a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka if (view.isKeyPreviewPopupEnabled() && mMoreKeys.length == 1 && previewWidth > 0 265a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka && previewHeight > 0) { 266a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka width = previewWidth; 267a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka height = previewHeight + mParams.mVerticalGap; 268a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka } else { 269a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka width = getMaxKeyWidth(view, parentKey.mMoreKeys, mParams.mDefaultKeyWidth); 270a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka height = parentKeyboard.mMostCommonKeyHeight; 271a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka } 272a0e4f40994f779ad98268921c63d6535ad04224fTadashi G. Takaoka mParams.setParameters(mMoreKeys.length, parentKey.mMaxMoreKeysColumn, width, height, 2736213983fa147d010ce30314f43b0a1800d2528baTadashi G. Takaoka parentKey.mX + parentKey.mWidth / 2, view.getMeasuredWidth()); 27432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 27532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 276bd7b160cfb05ee543e3cb6ddc7bd231b3f3aba0bTadashi G. Takaoka private static int getMaxKeyWidth(KeyboardView view, String[] moreKeys, int minKeyWidth) { 277be34d973349909196dc3427a5653f4e119092ea7Tadashi G. Takaoka final int padding = (int) view.getContext().getResources() 2782affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka .getDimension(R.dimen.more_keys_keyboard_key_horizontal_padding); 27932572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka Paint paint = null; 2802315bfc7c8df0f6d9fb627456f2a298f5580b52dTadashi G. Takaoka int maxWidth = minKeyWidth; 281bd7b160cfb05ee543e3cb6ddc7bd231b3f3aba0bTadashi G. Takaoka for (String moreKeySpec : moreKeys) { 282e01d272603f3643ce613e61dd3204379f4f4fb73Tadashi G. Takaoka final String label = KeySpecParser.getLabel(moreKeySpec); 283bd7b160cfb05ee543e3cb6ddc7bd231b3f3aba0bTadashi G. Takaoka // If the label is single letter, minKeyWidth is enough to hold the label. 28432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (label != null && label.length() > 1) { 28532572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka if (paint == null) { 28632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka paint = new Paint(); 28732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka paint.setAntiAlias(true); 28832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 289be34d973349909196dc3427a5653f4e119092ea7Tadashi G. Takaoka final int width = (int)view.getDefaultLabelWidth(label, paint) + padding; 2902315bfc7c8df0f6d9fb627456f2a298f5580b52dTadashi G. Takaoka if (maxWidth < width) { 2912315bfc7c8df0f6d9fb627456f2a298f5580b52dTadashi G. Takaoka maxWidth = width; 2922315bfc7c8df0f6d9fb627456f2a298f5580b52dTadashi G. Takaoka } 29332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 29432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 295be34d973349909196dc3427a5653f4e119092ea7Tadashi G. Takaoka return maxWidth; 29632572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 29732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka 29832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka @Override 2992affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka public MoreKeysKeyboard build() { 3002affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka final MoreKeysKeyboardParams params = mParams; 3019d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka for (int n = 0; n < mMoreKeys.length; n++) { 302bd7b160cfb05ee543e3cb6ddc7bd231b3f3aba0bTadashi G. Takaoka final String moreKeySpec = mMoreKeys[n]; 30332572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka final int row = n / params.mNumColumns; 3049d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka final Key key = new Key(mResources, params, moreKeySpec, params.getX(n, row), 3052fc4248700023853980b0006c12425079e3f9257Tadashi G. Takaoka params.getY(row), params.mDefaultKeyWidth, params.mDefaultRowHeight); 3062fc4248700023853980b0006c12425079e3f9257Tadashi G. Takaoka params.markAsEdgeKey(key, row); 30732572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka params.onAddKey(key); 30832572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 3092affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka return new MoreKeysKeyboard(params); 31032572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 31132572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka } 31204c96ab966e8a58e5cd401362b49509751ce75d9Tadashi G. Takaoka} 313