Auto mini keyboard layout

Bug: 4280617
Change-Id: I34c344cbf350fe125589aa14ad69e4bd1f4e6f66
main
Tadashi G. Takaoka 2011-04-13 17:57:29 +09:00
parent 72a82d7ee8
commit 5d542c2492
7 changed files with 1390 additions and 250 deletions

View File

@ -63,7 +63,7 @@
<!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. --> <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
<string name="config_default_keyboard_theme_id" translatable="false">4</string> <string name="config_default_keyboard_theme_id" translatable="false">4</string>
<string name="config_text_size_of_language_on_spacebar" translatable="false">small</string> <string name="config_text_size_of_language_on_spacebar" translatable="false">small</string>
<integer name="config_max_popup_keyboard_column">10</integer> <integer name="config_max_popup_keyboard_column">5</integer>
<!-- Whether or not auto-correction should be enabled by default --> <!-- Whether or not auto-correction should be enabled by default -->
<bool name="enable_autocorrect">true</bool> <bool name="enable_autocorrect">true</bool>
<string-array name="auto_correction_threshold_values" translatable="false"> <string-array name="auto_correction_threshold_values" translatable="false">

View File

@ -17,7 +17,6 @@
package com.android.inputmethod.keyboard; package com.android.inputmethod.keyboard;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
public abstract class KeyDetector { public abstract class KeyDetector {
@ -110,31 +109,4 @@ public abstract class KeyDetector {
* @return The nearest key index * @return The nearest key index
*/ */
abstract public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes); abstract public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes);
/**
* Compute the most common key width in order to use it as proximity key detection threshold.
*
* @param keyboard The keyboard to compute the most common key width
* @return The most common key width in the keyboard
*/
public static int getMostCommonKeyWidth(final Keyboard keyboard) {
if (keyboard == null) return 0;
final List<Key> keys = keyboard.getKeys();
if (keys == null || keys.size() == 0) return 0;
final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
int maxCount = 0;
int mostCommonWidth = 0;
for (final Key key : keys) {
final Integer width = key.mWidth + key.mGap;
Integer count = histogram.get(width);
if (count == null)
count = 0;
histogram.put(width, ++count);
if (count > maxCount) {
maxCount = count;
mostCommonWidth = width;
}
}
return mostCommonWidth + keyboard.getHorizontalGap();
}
} }

View File

@ -405,6 +405,29 @@ public class Keyboard {
return EMPTY_INT_ARRAY; return EMPTY_INT_ARRAY;
} }
/**
* Compute the most common key width in order to use it as proximity key detection threshold.
*
* @return The most common key width in the keyboard
*/
public int getMostCommonKeyWidth() {
final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
int maxCount = 0;
int mostCommonWidth = 0;
for (final Key key : mKeys) {
final Integer width = key.mWidth + key.mGap;
Integer count = histogram.get(width);
if (count == null)
count = 0;
histogram.put(width, ++count);
if (count > maxCount) {
maxCount = count;
mostCommonWidth = width;
}
}
return mostCommonWidth;
}
private void loadKeyboard(Context context, int xmlLayoutResId) { private void loadKeyboard(Context context, int xmlLayoutResId) {
try { try {
KeyboardParser parser = new KeyboardParser(this, context.getResources()); KeyboardParser parser = new KeyboardParser(this, context.getResources());

View File

@ -501,7 +501,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
requestLayout(); requestLayout();
mKeyboardChanged = true; mKeyboardChanged = true;
invalidateAllKeys(); invalidateAllKeys();
mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(keyboard)); mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
mMiniKeyboardCache.clear(); mMiniKeyboardCache.clear();
} }

View File

@ -34,7 +34,7 @@ public class MiniKeyboardBuilder {
/* package */ static class MiniKeyboardLayoutParams { /* package */ static class MiniKeyboardLayoutParams {
public final int mKeyWidth; public final int mKeyWidth;
public final int mRowHeight; public final int mRowHeight;
/* package */ final boolean mTopRowNeedsCentering; /* package */ final int mTopRowAdjustment;
public final int mNumRows; public final int mNumRows;
public final int mNumColumns; public final int mNumColumns;
public final int mLeftKeys; public final int mLeftKeys;
@ -55,29 +55,52 @@ public class MiniKeyboardBuilder {
if (parentKeyboardWidth / keyWidth < maxColumns) if (parentKeyboardWidth / keyWidth < maxColumns)
throw new IllegalArgumentException("Keyboard is too small to hold mini keyboard: " throw new IllegalArgumentException("Keyboard is too small to hold mini keyboard: "
+ parentKeyboardWidth + " " + keyWidth + " " + maxColumns); + parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
final int numRows = (numKeys + maxColumns - 1) / maxColumns;
mKeyWidth = keyWidth; mKeyWidth = keyWidth;
mRowHeight = rowHeight; mRowHeight = rowHeight;
mNumRows = numRows;
final int numColumns = Math.min(numKeys, maxColumns); final int numRows = (numKeys + maxColumns - 1) / maxColumns;
final int topRowKeys = numKeys % numColumns; mNumRows = numRows;
final int numColumns = getOptimizedColumns(numKeys, maxColumns);
mNumColumns = numColumns; mNumColumns = numColumns;
mTopRowNeedsCentering = topRowKeys != 0 && (numColumns - topRowKeys) % 2 != 0;
final int numLeftKeys = (numColumns - 1) / 2; final int numLeftKeys = (numColumns - 1) / 2;
final int numRightKeys = numColumns - numLeftKeys; // including default key. final int numRightKeys = numColumns - numLeftKeys; // including default key.
final int maxLeftKeys = coordXInParent / keyWidth; final int maxLeftKeys = coordXInParent / keyWidth;
final int maxRightKeys = Math.max(1, (parentKeyboardWidth - coordXInParent) / keyWidth); final int maxRightKeys = Math.max(1, (parentKeyboardWidth - coordXInParent) / keyWidth);
int leftKeys, rightKeys;
if (numLeftKeys > maxLeftKeys) { if (numLeftKeys > maxLeftKeys) {
mLeftKeys = maxLeftKeys; leftKeys = maxLeftKeys;
mRightKeys = numColumns - maxLeftKeys; rightKeys = numColumns - maxLeftKeys;
} else if (numRightKeys > maxRightKeys) { } else if (numRightKeys > maxRightKeys) {
mLeftKeys = numColumns - maxRightKeys; leftKeys = numColumns - maxRightKeys;
mRightKeys = maxRightKeys; rightKeys = maxRightKeys;
} else { } else {
mLeftKeys = numLeftKeys; leftKeys = numLeftKeys;
mRightKeys = numRightKeys; rightKeys = numRightKeys;
}
// Shift right if the left edge of mini keyboard is on the edge of parent keyboard
// unless the parent key is on the left edge.
if (leftKeys * keyWidth >= coordXInParent && leftKeys > 0) {
leftKeys--;
rightKeys++;
}
// Shift left if the right edge of mini keyboard is on the edge of parent keyboard
// unless the parent key is on the right edge.
if (rightKeys * keyWidth + coordXInParent >= parentKeyboardWidth && rightKeys > 1) {
leftKeys++;
rightKeys--;
}
mLeftKeys = leftKeys;
mRightKeys = rightKeys;
// Centering of the top row.
final boolean onEdge = (leftKeys == 0 || rightKeys == 1);
if (numRows < 2 || onEdge || getTopRowEmptySlots(numKeys, numColumns) % 2 == 0) {
mTopRowAdjustment = 0;
} else if (mLeftKeys < mRightKeys - 1) {
mTopRowAdjustment = 1;
} else {
mTopRowAdjustment = -1;
} }
} }
@ -113,14 +136,32 @@ public class MiniKeyboardBuilder {
return pos; return pos;
} }
private static int getTopRowEmptySlots(int numKeys, int numColumns) {
final int remainingKeys = numKeys % numColumns;
if (remainingKeys == 0) {
return 0;
} else {
return numColumns - remainingKeys;
}
}
private int getOptimizedColumns(int numKeys, int maxColumns) {
int numColumns = Math.min(numKeys, maxColumns);
while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
numColumns--;
}
return numColumns;
}
public int getDefaultKeyCoordX() { public int getDefaultKeyCoordX() {
return mLeftKeys * mKeyWidth; return mLeftKeys * mKeyWidth;
} }
public int getX(int n, int row) { public int getX(int n, int row) {
final int x = getColumnPos(n) * mKeyWidth + getDefaultKeyCoordX(); final int x = getColumnPos(n) * mKeyWidth + getDefaultKeyCoordX();
if (isLastRow(row) && mTopRowNeedsCentering) if (isTopRow(row)) {
return x - mKeyWidth / 2; return x + mTopRowAdjustment * (mKeyWidth / 2);
}
return x; return x;
} }
@ -131,27 +172,27 @@ public class MiniKeyboardBuilder {
public int getRowFlags(int row) { public int getRowFlags(int row) {
int rowFlags = 0; int rowFlags = 0;
if (row == 0) rowFlags |= Keyboard.EDGE_TOP; if (row == 0) rowFlags |= Keyboard.EDGE_TOP;
if (isLastRow(row)) rowFlags |= Keyboard.EDGE_BOTTOM; if (isTopRow(row)) rowFlags |= Keyboard.EDGE_BOTTOM;
return rowFlags; return rowFlags;
} }
private boolean isLastRow(int rowCount) { private boolean isTopRow(int rowCount) {
return rowCount == mNumRows - 1; return rowCount == mNumRows - 1;
} }
} }
public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key popupKey) { public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey) {
final Context context = view.getContext(); final Context context = view.getContext();
mRes = context.getResources(); mRes = context.getResources();
final MiniKeyboard keyboard = new MiniKeyboard(context, layoutTemplateResId, null); final MiniKeyboard keyboard = new MiniKeyboard(context, layoutTemplateResId, null);
mKeyboard = keyboard; mKeyboard = keyboard;
mPopupCharacters = popupKey.mPopupCharacters; mPopupCharacters = parentKey.mPopupCharacters;
final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth()); final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth());
final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams( final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams(
mPopupCharacters.length, popupKey.mMaxPopupColumn, mPopupCharacters.length, parentKey.mMaxPopupColumn,
keyWidth, keyboard.getRowHeight(), keyWidth, keyboard.getRowHeight(),
popupKey.mX + (popupKey.mWidth + popupKey.mGap) / 2 - keyWidth / 2, parentKey.mX + (parentKey.mWidth + parentKey.mGap) / 2 - keyWidth / 2,
view.getMeasuredWidth()); view.getMeasuredWidth());
mParams = params; mParams = params;

View File

@ -55,7 +55,7 @@ public class SuggestHelper {
mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL); mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL);
mKeyDetector.setKeyboard(mKeyboard, 0, 0); mKeyDetector.setKeyboard(mKeyboard, 0, 0);
mKeyDetector.setProximityCorrectionEnabled(true); mKeyDetector.setProximityCorrectionEnabled(true);
mKeyDetector.setProximityThreshold(KeyDetector.getMostCommonKeyWidth(mKeyboard)); mKeyDetector.setProximityThreshold(mKeyboard.getMostCommonKeyWidth());
} }
public void setCorrectionMode(int correctionMode) { public void setCorrectionMode(int correctionMode) {