Make Keyboard object immutable except shift state

This is the first step to implement suggestions pane as mini keyboard.

Bug: 5023981
Change-Id: I90ffbde0fda19b4be68add449310997b56bf6904
main
Tadashi G. Takaoka 2011-07-28 17:05:40 -07:00
parent 8d7782bf3c
commit 8da9a13760
19 changed files with 345 additions and 534 deletions

View File

@ -30,3 +30,7 @@
-keep class com.android.inputmethod.latin.SettingsActivity { -keep class com.android.inputmethod.latin.SettingsActivity {
*; *;
} }
-keep class com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder$MiniKeyboardLayoutParams {
<init>(...);
}

View File

@ -27,6 +27,7 @@ import android.util.Xml;
import com.android.inputmethod.keyboard.internal.KeyStyles; import com.android.inputmethod.keyboard.internal.KeyStyles;
import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.keyboard.internal.KeyboardParser; import com.android.inputmethod.keyboard.internal.KeyboardParser;
import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException; import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException;
import com.android.inputmethod.keyboard.internal.PopupCharactersParser; import com.android.inputmethod.keyboard.internal.PopupCharactersParser;
@ -190,11 +191,11 @@ public class Key {
/** /**
* This constructor is being used only for key in popup mini keyboard. * This constructor is being used only for key in popup mini keyboard.
*/ */
public Key(Resources res, Keyboard keyboard, CharSequence popupCharacter, int x, int y, public Key(Resources res, KeyboardParams params, CharSequence popupCharacter, int x, int y,
int width, int height, int edgeFlags) { int width, int height, int edgeFlags) {
mHeight = height - keyboard.getVerticalGap(); mHeight = height - params.mVerticalGap;
mHorizontalGap = keyboard.getHorizontalGap(); mHorizontalGap = params.mHorizontalGap;
mVerticalGap = keyboard.getVerticalGap(); mVerticalGap = params.mVerticalGap;
mVisualInsetsLeft = mVisualInsetsRight = 0; mVisualInsetsLeft = mVisualInsetsRight = 0;
mWidth = width - mHorizontalGap; mWidth = width - mHorizontalGap;
mEdgeFlags = edgeFlags; mEdgeFlags = edgeFlags;
@ -209,8 +210,8 @@ public class Key {
mLabel = PopupCharactersParser.getLabel(popupSpecification); mLabel = PopupCharactersParser.getLabel(popupSpecification);
mOutputText = PopupCharactersParser.getOutputText(popupSpecification); mOutputText = PopupCharactersParser.getOutputText(popupSpecification);
final int code = PopupCharactersParser.getCode(res, popupSpecification); final int code = PopupCharactersParser.getCode(res, popupSpecification);
mCode = keyboard.isRtlKeyboard() ? getRtlParenthesisCode(code) : code; mCode = params.mIsRtlKeyboard ? getRtlParenthesisCode(code) : code;
mIcon = keyboard.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification)); mIcon = params.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification));
// Horizontal gap is divided equally to both sides of the key. // Horizontal gap is divided equally to both sides of the key.
mX = x + mHorizontalGap / 2; mX = x + mHorizontalGap / 2;
mY = y; mY = y;
@ -220,16 +221,15 @@ public class Key {
* Create a key with the given top-left coordinate and extract its attributes from the XML * Create a key with the given top-left coordinate and extract its attributes from the XML
* parser. * parser.
* @param res resources associated with the caller's context * @param res resources associated with the caller's context
* @param row the row that this key belongs to. The row must already be attached to * @param params the keyboard building parameters.
* a {@link Keyboard}. * @param row the row that this key belongs to.
* @param x the x coordinate of the top-left * @param x the x coordinate of the top-left
* @param y the y coordinate of the top-left * @param y the y coordinate of the top-left
* @param parser the XML parser containing the attributes for this key * @param parser the XML parser containing the attributes for this key
* @param keyStyles active key styles set * @param keyStyles active key styles set
*/ */
public Key(Resources res, Row row, int x, int y, XmlResourceParser parser, public Key(Resources res, KeyboardParams params, Row row, int x, int y,
KeyStyles keyStyles) { XmlResourceParser parser, KeyStyles keyStyles) {
final Keyboard keyboard = row.getKeyboard();
final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser), final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard); R.styleable.Keyboard);
@ -237,14 +237,14 @@ public class Key {
try { try {
mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr, mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_rowHeight, R.styleable.Keyboard_rowHeight,
keyboard.getKeyboardHeight(), row.mDefaultHeight) - keyboard.getVerticalGap(); params.mHeight, row.mRowHeight) - params.mVerticalGap;
mHorizontalGap = KeyboardParser.getDimensionOrFraction(keyboardAttr, mHorizontalGap = KeyboardParser.getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_horizontalGap, R.styleable.Keyboard_horizontalGap,
keyboard.getDisplayWidth(), keyboard.getHorizontalGap()); params.mWidth, params.mHorizontalGap);
mVerticalGap = keyboard.getVerticalGap(); mVerticalGap = params.mVerticalGap;
keyWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr, keyWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_keyWidth, R.styleable.Keyboard_keyWidth,
keyboard.getDisplayWidth(), row.mDefaultWidth); params.mWidth, row.mDefaultKeyWidth);
} finally { } finally {
keyboardAttr.recycle(); keyboardAttr.recycle();
} }
@ -262,7 +262,7 @@ public class Key {
style = keyStyles.getEmptyKeyStyle(); style = keyStyles.getEmptyKeyStyle();
} }
final int keyboardWidth = keyboard.getDisplayWidth(); final int keyboardWidth = params.mOccupiedWidth;
int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr, int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr,
R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x); R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x);
if (keyXPos < 0) { if (keyXPos < 0) {
@ -293,13 +293,13 @@ public class Key {
CharSequence[] popupCharacters = style.getTextArray( CharSequence[] popupCharacters = style.getTextArray(
keyAttr, R.styleable.Keyboard_Key_popupCharacters); keyAttr, R.styleable.Keyboard_Key_popupCharacters);
if (keyboard.mId.mPasswordInput) { if (params.mId.mPasswordInput) {
popupCharacters = PopupCharactersParser.filterOut( popupCharacters = PopupCharactersParser.filterOut(
res, popupCharacters, PopupCharactersParser.NON_ASCII_FILTER); res, popupCharacters, PopupCharactersParser.NON_ASCII_FILTER);
} }
// In Arabic symbol layouts, we'd like to keep digits in popup characters regardless of // In Arabic symbol layouts, we'd like to keep digits in popup characters regardless of
// config_digit_popup_characters_enabled. // config_digit_popup_characters_enabled.
if (keyboard.mId.isAlphabetKeyboard() && !res.getBoolean( if (params.mId.isAlphabetKeyboard() && !res.getBoolean(
R.bool.config_digit_popup_characters_enabled)) { R.bool.config_digit_popup_characters_enabled)) {
mPopupCharacters = PopupCharactersParser.filterOut( mPopupCharacters = PopupCharactersParser.filterOut(
res, popupCharacters, PopupCharactersParser.DIGIT_FILTER); res, popupCharacters, PopupCharactersParser.DIGIT_FILTER);
@ -308,7 +308,7 @@ public class Key {
} }
mMaxPopupColumn = style.getInt(keyboardAttr, mMaxPopupColumn = style.getInt(keyboardAttr,
R.styleable.Keyboard_Key_maxPopupKeyboardColumn, R.styleable.Keyboard_Key_maxPopupKeyboardColumn,
keyboard.getMaxPopupKeyboardColumn()); params.mMaxPopupColumn);
mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false); mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false);
mFunctional = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isFunctional, false); mFunctional = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isFunctional, false);
@ -316,7 +316,7 @@ public class Key {
mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true); mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true);
mEdgeFlags = 0; mEdgeFlags = 0;
final KeyboardIconsSet iconsSet = keyboard.mIconsSet; final KeyboardIconsSet iconsSet = params.mIconsSet;
mVisualInsetsLeft = KeyboardParser.getDimensionOrFraction(keyAttr, mVisualInsetsLeft = KeyboardParser.getDimensionOrFraction(keyAttr,
R.styleable.Keyboard_Key_visualInsetsLeft, keyboardWidth, 0); R.styleable.Keyboard_Key_visualInsetsLeft, keyboardWidth, 0);
mVisualInsetsRight = KeyboardParser.getDimensionOrFraction(keyAttr, mVisualInsetsRight = KeyboardParser.getDimensionOrFraction(keyAttr,
@ -331,7 +331,7 @@ public class Key {
KeyboardIconsSet.ICON_UNDEFINED); KeyboardIconsSet.ICON_UNDEFINED);
if (shiftedIconId != KeyboardIconsSet.ICON_UNDEFINED) { if (shiftedIconId != KeyboardIconsSet.ICON_UNDEFINED) {
final Drawable shiftedIcon = iconsSet.getIcon(shiftedIconId); final Drawable shiftedIcon = iconsSet.getIcon(shiftedIconId);
keyboard.addShiftedIcon(this, shiftedIcon); params.addShiftedIcon(this, shiftedIcon);
} }
mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
@ -344,15 +344,12 @@ public class Key {
Keyboard.CODE_UNSPECIFIED); Keyboard.CODE_UNSPECIFIED);
if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) { if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) {
final int firstChar = mLabel.charAt(0); final int firstChar = mLabel.charAt(0);
mCode = keyboard.isRtlKeyboard() ? getRtlParenthesisCode(firstChar) : firstChar; mCode = params.mIsRtlKeyboard ? getRtlParenthesisCode(firstChar) : firstChar;
} else if (code != Keyboard.CODE_UNSPECIFIED) { } else if (code != Keyboard.CODE_UNSPECIFIED) {
mCode = code; mCode = code;
} else { } else {
mCode = Keyboard.CODE_DUMMY; mCode = Keyboard.CODE_DUMMY;
} }
if (mCode == Keyboard.CODE_SHIFT) {
keyboard.addShiftKey(this);
}
} finally { } finally {
keyAttr.recycle(); keyAttr.recycle();
} }

View File

@ -57,7 +57,7 @@ public class KeyDetector {
mCorrectionX = (int)correctionX; mCorrectionX = (int)correctionX;
mCorrectionY = (int)correctionY; mCorrectionY = (int)correctionY;
mKeyboard = keyboard; mKeyboard = keyboard;
final int threshold = keyboard.getMostCommonKeyWidth(); final int threshold = keyboard.mMostCommonKeyWidth;
mProximityThresholdSquare = threshold * threshold; mProximityThresholdSquare = threshold * threshold;
} }

View File

@ -16,23 +16,14 @@
package com.android.inputmethod.keyboard; package com.android.inputmethod.keyboard;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParser; import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.keyboard.internal.KeyboardShiftState; import com.android.inputmethod.keyboard.internal.KeyboardShiftState;
import com.android.inputmethod.latin.R;
import org.xmlpull.v1.XmlPullParserException; import java.util.Collections;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -56,8 +47,6 @@ import java.util.Set;
* </pre> * </pre>
*/ */
public class Keyboard { public class Keyboard {
private static final String TAG = Keyboard.class.getSimpleName();
public static final int EDGE_LEFT = 0x01; public static final int EDGE_LEFT = 0x01;
public static final int EDGE_RIGHT = 0x02; public static final int EDGE_RIGHT = 0x02;
public static final int EDGE_TOP = 0x04; public static final int EDGE_TOP = 0x04;
@ -98,209 +87,77 @@ public class Keyboard {
public final KeyboardId mId; public final KeyboardId mId;
/** Total height of the keyboard, including the padding and keys */ /** Total height of the keyboard, including the padding and keys */
private int mTotalHeight; public final int mOccupiedHeight;
/** Total width of the keyboard, including the padding and keys */
public final int mOccupiedWidth;
/** public final int mHeight;
* Total width (minimum width) of the keyboard, including left side gaps and keys, but not any public final int mWidth;
* gaps on the right side.
*/
private int mMinWidth;
/** Horizontal gap default for all rows */
private int mHorizontalGap;
/** Default key width */
private int mDefaultKeyWidth;
/** Default row height */ /** Default row height */
private int mDefaultRowHeight; public final int mDefaultRowHeight;
/** Default gap between rows */ /** Default gap between rows */
private int mVerticalGap; public final int mVerticalGap;
public final int mMostCommonKeyWidth;
/** Popup keyboard template */ /** Popup keyboard template */
private int mPopupKeyboardResId; public final int mPopupKeyboardResId;
/** Maximum column for popup keyboard */ /** Maximum column for popup keyboard */
private int mMaxPopupColumn; public final int mMaxPopupColumn;
/** True if Right-To-Left keyboard */ /** True if Right-To-Left keyboard */
private boolean mIsRtlKeyboard; public final boolean mIsRtlKeyboard;
/** List of keys in this keyboard */ /** List of keys and icons in this keyboard */
private final List<Key> mKeys = new ArrayList<Key>(); public final List<Key> mKeys;
/** List of shift keys in this keyboard and its icons and state */ public final List<Key> mShiftKeys;
private final List<Key> mShiftKeys = new ArrayList<Key>(); public final Set<Key> mShiftLockKeys;
private final Map<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>(); public final Map<Key, Drawable> mShiftedIcons;
private final Map<Key, Drawable> mUnshiftedIcons = new HashMap<Key, Drawable>(); public final Map<Key, Drawable> mUnshiftedIcons;
private final Set<Key> mShiftLockKeys = new HashSet<Key>(); public final KeyboardIconsSet mIconsSet;
public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
/** Width of the screen available to fit the keyboard */
private final int mDisplayWidth;
/** Height of the screen */
private final int mDisplayHeight;
/** Height of keyboard */
private int mKeyboardHeight;
private int mMostCommonKeyWidth = 0;
private final KeyboardShiftState mShiftState = new KeyboardShiftState(); private final KeyboardShiftState mShiftState = new KeyboardShiftState();
// Variables for pre-computing nearest keys.
// TODO: Change GRID_WIDTH and GRID_HEIGHT to private.
public final int GRID_WIDTH;
public final int GRID_HEIGHT;
private final ProximityInfo mProximityInfo; private final ProximityInfo mProximityInfo;
/** public Keyboard(KeyboardParams params) {
* Creates a keyboard from the given xml key layout file. mId = params.mId;
* @param context the application or service context mOccupiedHeight = params.mOccupiedHeight;
* @param xmlLayoutResId the resource file that contains the keyboard layout and keys. mOccupiedWidth = params.mOccupiedWidth;
* @param id keyboard identifier mHeight = params.mHeight;
* @param width keyboard width mWidth = params.mWidth;
*/ mMostCommonKeyWidth = params.mMostCommonKeyWidth;
mIsRtlKeyboard = params.mIsRtlKeyboard;
mPopupKeyboardResId = params.mPopupKeyboardResId;
mMaxPopupColumn = params.mMaxPopupColumn;
public Keyboard(Context context, int xmlLayoutResId, KeyboardId id, int width) { mDefaultRowHeight = params.mDefaultRowHeight;
final Resources res = context.getResources(); mVerticalGap = params.mVerticalGap;
GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
final int horizontalEdgesPadding = (int)res.getDimension( mKeys = Collections.unmodifiableList(params.mKeys);
R.dimen.keyboard_horizontal_edges_padding); mShiftKeys = Collections.unmodifiableList(params.mShiftKeys);
mDisplayWidth = width - horizontalEdgesPadding * 2; mShiftLockKeys = Collections.unmodifiableSet(params.mShiftLockKeys);
// TODO: Adjust the height by referring to the height of area available for drawing as well. mShiftedIcons = Collections.unmodifiableMap(params.mShiftedIcons);
mDisplayHeight = res.getDisplayMetrics().heightPixels; mUnshiftedIcons = Collections.unmodifiableMap(params.mUnshiftedIcons);
mIconsSet = params.mIconsSet;
mHorizontalGap = 0;
setKeyWidth(mDisplayWidth / 10);
mVerticalGap = 0;
mDefaultRowHeight = mDefaultKeyWidth;
mId = id;
loadKeyboard(context, xmlLayoutResId);
mProximityInfo = new ProximityInfo( mProximityInfo = new ProximityInfo(
GRID_WIDTH, GRID_HEIGHT, getMinWidth(), getHeight(), getKeyWidth(), mKeys); params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
mMostCommonKeyWidth, mKeys);
} }
public int getProximityInfo() { public int getProximityInfo() {
return mProximityInfo.getNativeProximityInfo(); return mProximityInfo.getNativeProximityInfo();
} }
// TODO: Access mKeys directly
public List<Key> getKeys() { public List<Key> getKeys() {
return mKeys; return mKeys;
} }
public int getHorizontalGap() {
return mHorizontalGap;
}
public void setHorizontalGap(int gap) {
mHorizontalGap = gap;
}
public int getVerticalGap() {
return mVerticalGap;
}
public void setVerticalGap(int gap) {
mVerticalGap = gap;
}
public int getRowHeight() {
return mDefaultRowHeight;
}
public void setRowHeight(int height) {
mDefaultRowHeight = height;
}
public int getKeyWidth() {
return mDefaultKeyWidth;
}
public void setKeyWidth(int width) {
mDefaultKeyWidth = width;
}
/**
* Returns the total height of the keyboard
* @return the total height of the keyboard
*/
public int getHeight() {
return mTotalHeight;
}
public void setHeight(int height) {
mTotalHeight = height;
}
public int getMinWidth() {
return mMinWidth;
}
public void setMinWidth(int minWidth) {
mMinWidth = minWidth;
}
public int getDisplayHeight() {
return mDisplayHeight;
}
public int getDisplayWidth() {
return mDisplayWidth;
}
public int getKeyboardHeight() {
return mKeyboardHeight;
}
public void setKeyboardHeight(int height) {
mKeyboardHeight = height;
}
public boolean isRtlKeyboard() {
return mIsRtlKeyboard;
}
public void setRtlKeyboard(boolean isRtl) {
mIsRtlKeyboard = isRtl;
}
public int getPopupKeyboardResId() {
return mPopupKeyboardResId;
}
public void setPopupKeyboardResId(int resId) {
mPopupKeyboardResId = resId;
}
public int getMaxPopupKeyboardColumn() {
return mMaxPopupColumn;
}
public void setMaxPopupKeyboardColumn(int column) {
mMaxPopupColumn = column;
}
public void addShiftKey(Key key) {
if (key == null) return;
mShiftKeys.add(key);
if (key.mSticky) {
mShiftLockKeys.add(key);
}
}
public void addShiftedIcon(Key key, Drawable icon) {
if (key == null) return;
mUnshiftedIcons.put(key, key.getIcon());
mShiftedIcons.put(key, icon);
}
public boolean hasShiftLockKey() { public boolean hasShiftLockKey() {
return !mShiftLockKeys.isEmpty(); return !mShiftLockKeys.isEmpty();
} }
@ -391,46 +248,4 @@ public class Keyboard {
public int[] getNearestKeys(int x, int y) { public int[] getNearestKeys(int x, int y) {
return mProximityInfo.getNearestKeys(x, y); return mProximityInfo.getNearestKeys(x, y);
} }
/**
* 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() {
if (mMostCommonKeyWidth == 0) {
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.mHorizontalGap;
Integer count = histogram.get(width);
if (count == null)
count = 0;
histogram.put(width, ++count);
if (count > maxCount) {
maxCount = count;
mostCommonWidth = width;
}
}
mMostCommonKeyWidth = mostCommonWidth;
}
return mMostCommonKeyWidth;
}
private void loadKeyboard(Context context, int xmlLayoutResId) {
try {
KeyboardParser parser = new KeyboardParser(this, context);
parser.parseKeyboard(xmlLayoutResId);
// mMinWidth is the width of this keyboard which is maximum width of row.
mMinWidth = parser.getMaxRowWidth();
mTotalHeight = parser.getTotalHeight();
} catch (XmlPullParserException e) {
Log.w(TAG, "keyboard XML parse error: " + e);
throw new IllegalArgumentException(e);
} catch (IOException e) {
Log.w(TAG, "keyboard XML parse error: " + e);
throw new RuntimeException(e);
}
}
} }

View File

@ -42,8 +42,6 @@ public class KeyboardId {
public static final int F2KEY_MODE_SHORTCUT_IME = 2; public static final int F2KEY_MODE_SHORTCUT_IME = 2;
public static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3; public static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3;
private static final int MINI_KEYBOARD_ID_MARKER = -1;
public final Locale mLocale; public final Locale mLocale;
public final int mOrientation; public final int mOrientation;
public final int mWidth; public final int mWidth;
@ -110,9 +108,9 @@ public class KeyboardId {
}); });
} }
public KeyboardId cloneAsMiniKeyboard() { public KeyboardId cloneWithNewXml(String xmlName, int xmlId) {
return new KeyboardId("mini popup keyboard", MINI_KEYBOARD_ID_MARKER, mLocale, mOrientation, return new KeyboardId(xmlName, xmlId, mLocale, mOrientation, mWidth, mMode, mAttribute,
mWidth, mMode, mAttribute, false, F2KEY_MODE_NONE, false, false, false); false, F2KEY_MODE_NONE, false, false, false);
} }
public KeyboardId cloneWithNewGeometry(int orientation, int width) { public KeyboardId cloneWithNewGeometry(int orientation, int width) {
@ -127,10 +125,6 @@ public class KeyboardId {
return mXmlId; return mXmlId;
} }
public boolean isMiniKeyboard() {
return mXmlId == MINI_KEYBOARD_ID_MARKER;
}
public boolean isAlphabetKeyboard() { public boolean isAlphabetKeyboard() {
return mXmlId == R.xml.kbd_qwerty; return mXmlId == R.xml.kbd_qwerty;
} }

View File

@ -29,7 +29,6 @@ import android.view.View;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
import com.android.inputmethod.keyboard.internal.ModifierKeyState; import com.android.inputmethod.keyboard.internal.ModifierKeyState;
import com.android.inputmethod.keyboard.internal.ShiftKeyState; import com.android.inputmethod.keyboard.internal.ShiftKeyState;
import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinIME;
@ -270,14 +269,17 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
if (keyboard == null) { if (keyboard == null) {
final Locale savedLocale = Utils.setSystemLocale( final Locale savedLocale = Utils.setSystemLocale(
mResources, mSubtypeSwitcher.getInputLocale()); mResources, mSubtypeSwitcher.getInputLocale());
try {
keyboard = new LatinKeyboard(mThemeContext, id, id.mWidth); keyboard = new LatinKeyboard.Builder(mThemeContext).load(id).build();
} finally {
Utils.setSystemLocale(mResources, savedLocale);
}
mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard)); mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
if (DEBUG_CACHE)
if (DEBUG_CACHE) {
Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": " Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
+ ((ref == null) ? "LOAD" : "GCed") + " id=" + id); + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
}
Utils.setSystemLocale(mResources, savedLocale);
} else if (DEBUG_CACHE) { } else if (DEBUG_CACHE) {
Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT id=" + id); Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT id=" + id);
} }

View File

@ -357,7 +357,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mDirtyRect.set(0, 0, getWidth(), getHeight()); mDirtyRect.set(0, 0, getWidth(), getHeight());
mBufferNeedsUpdate = true; mBufferNeedsUpdate = true;
invalidateAllKeys(); invalidateAllKeys();
final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap(); final int keyHeight = keyboard.mDefaultRowHeight - keyboard.mVerticalGap;
mKeyDrawParams.updateKeyHeight(keyHeight); mKeyDrawParams.updateKeyHeight(keyHeight);
mKeyPreviewDrawParams.updateKeyHeight(keyHeight); mKeyPreviewDrawParams.updateKeyHeight(keyHeight);
} }
@ -396,7 +396,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mKeyboard != null) { if (mKeyboard != null) {
// The main keyboard expands to the display width. // The main keyboard expands to the display width.
final int height = mKeyboard.getKeyboardHeight() + getPaddingTop() + getPaddingBottom(); final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
setMeasuredDimension(widthMeasureSpec, height); setMeasuredDimension(widthMeasureSpec, height);
} else { } else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec);

View File

@ -31,13 +31,14 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.text.TextUtils; import android.text.TextUtils;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.keyboard.internal.KeyboardParser;
import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.SubtypeSwitcher;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Locale; import java.util.Locale;
// TODO: We should remove this class // TODO: We should remove this class
@ -73,32 +74,16 @@ public class LatinKeyboard extends Keyboard {
private static final String SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "small"; private static final String SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "small";
private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium"; private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium";
public LatinKeyboard(Context context, KeyboardId id, int width) { private LatinKeyboard(Context context, LatinKeyboardParams params) {
super(context, id.getXmlId(), id, width); super(params);
mRes = context.getResources(); mRes = context.getResources();
mTheme = context.getTheme(); mTheme = context.getTheme();
final List<Key> keys = getKeys();
int spaceKeyIndex = -1;
int shortcutKeyIndex = -1;
final int keyCount = keys.size();
for (int index = 0; index < keyCount; index++) {
// For now, assuming there are up to one space key and one shortcut key respectively.
switch (keys.get(index).mCode) {
case CODE_SPACE:
spaceKeyIndex = index;
break;
case CODE_SHORTCUT:
shortcutKeyIndex = index;
break;
}
}
// The index of space key is available only after Keyboard constructor has finished. // The index of space key is available only after Keyboard constructor has finished.
mSpaceKey = (spaceKeyIndex >= 0) ? keys.get(spaceKeyIndex) : null; mSpaceKey = params.mSpaceKey;
mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon() : null; mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon() : null;
mShortcutKey = (shortcutKeyIndex >= 0) ? keys.get(shortcutKeyIndex) : null; mShortcutKey = params.mShortcutKey;
mEnabledShortcutIcon = (mShortcutKey != null) ? mShortcutKey.getIcon() : null; mEnabledShortcutIcon = (mShortcutKey != null) ? mShortcutKey.getIcon() : null;
final TypedArray a = context.obtainStyledAttributes( final TypedArray a = context.obtainStyledAttributes(
@ -114,6 +99,42 @@ public class LatinKeyboard extends Keyboard {
a.recycle(); a.recycle();
} }
private static class LatinKeyboardParams extends KeyboardParams {
public Key mSpaceKey = null;
public Key mShortcutKey = null;
@Override
public void onAddKey(Key key) {
super.onAddKey(key);
switch (key.mCode) {
case Keyboard.CODE_SPACE:
mSpaceKey = key;
break;
case Keyboard.CODE_SHORTCUT:
mShortcutKey = key;
break;
}
}
}
public static class Builder extends KeyboardParser<LatinKeyboardParams> {
public Builder(Context context) {
super(context, new LatinKeyboardParams());
}
@Override
public Builder load(KeyboardId id) {
super.load(id);
return this;
}
@Override
public LatinKeyboard build() {
return new LatinKeyboard(mContext, mParams);
}
}
public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboardView view) { public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboardView view) {
mSpacebarTextFadeFactor = fadeFactor; mSpacebarTextFadeFactor = fadeFactor;
updateSpacebarForLocale(false); updateSpacebarForLocale(false);
@ -294,8 +315,8 @@ public class LatinKeyboard extends Keyboard {
@Override @Override
public int[] getNearestKeys(int x, int y) { public int[] getNearestKeys(int x, int y) {
// Avoid dead pixels at edges of the keyboard // Avoid dead pixels at edges of the keyboard
return super.getNearestKeys(Math.max(0, Math.min(x, getMinWidth() - 1)), return super.getNearestKeys(Math.max(0, Math.min(x, mOccupiedWidth - 1)),
Math.max(0, Math.min(y, getHeight() - 1))); Math.max(0, Math.min(y, mOccupiedHeight - 1)));
} }
public static int getTextSizeFromTheme(Theme theme, int style, int defValue) { public static int getTextSizeFromTheme(Theme theme, int style, int defValue) {

View File

@ -289,7 +289,7 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
super.setKeyboard(keyboard); super.setKeyboard(keyboard);
mKeyDetector.setKeyboard( mKeyDetector.setKeyboard(
keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection); keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth()); mKeyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
PointerTracker.setKeyDetector(mKeyDetector); PointerTracker.setKeyDetector(mKeyDetector);
mPopupPanelCache.clear(); mPopupPanelCache.clear();
} }
@ -360,7 +360,7 @@ public class LatinKeyboardBaseView extends KeyboardView implements PointerTracke
(PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view); (PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view);
final Keyboard parentKeyboard = getKeyboard(); final Keyboard parentKeyboard = getKeyboard();
final Keyboard miniKeyboard = new MiniKeyboardBuilder( final Keyboard miniKeyboard = new MiniKeyboardBuilder(
this, parentKeyboard.getPopupKeyboardResId(), parentKey, parentKeyboard).build(); this, parentKeyboard.mPopupKeyboardResId, parentKey, parentKeyboard).build();
miniKeyboardView.setKeyboard(miniKeyboard); miniKeyboardView.setKeyboard(miniKeyboard);
container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),

View File

@ -69,7 +69,7 @@ public class LatinKeyboardView extends LatinKeyboardBaseView {
public void setKeyboard(Keyboard newKeyboard) { public void setKeyboard(Keyboard newKeyboard) {
super.setKeyboard(newKeyboard); super.setKeyboard(newKeyboard);
// One-seventh of the keyboard width seems like a reasonable threshold // One-seventh of the keyboard width seems like a reasonable threshold
final int jumpThreshold = newKeyboard.getMinWidth() / 7; final int jumpThreshold = newKeyboard.mOccupiedWidth / 7;
mJumpThresholdSquare = jumpThreshold * jumpThreshold; mJumpThresholdSquare = jumpThreshold * jumpThreshold;
} }

View File

@ -16,30 +16,14 @@
package com.android.inputmethod.keyboard; package com.android.inputmethod.keyboard;
import android.content.Context; import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardLayoutParams;
public class MiniKeyboard extends Keyboard { public class MiniKeyboard extends Keyboard {
private int mDefaultKeyCoordX; private final int mDefaultKeyCoordX;
public MiniKeyboard(Context context, int xmlLayoutResId, Keyboard parentKeyboard) { public MiniKeyboard(MiniKeyboardLayoutParams params) {
super(context, xmlLayoutResId, parentKeyboard.mId.cloneAsMiniKeyboard(), super(params);
parentKeyboard.getMinWidth()); mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2;
// HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal
// and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are
// needed to keep having the same horizontal and vertical key spacing.
setHorizontalGap(0);
setVerticalGap(parentKeyboard.getVerticalGap() / 2);
// TODO: When we have correctly padded key background 9-patch drawables for mini keyboard,
// revert the above hacks and uncomment the following lines.
//setHorizontalGap(parentKeyboard.getHorizontalGap());
//setVerticalGap(parentKeyboard.getVerticalGap());
setRtlKeyboard(parentKeyboard.isRtlKeyboard());
}
public void setDefaultCoordX(int pos) {
mDefaultKeyCoordX = pos;
} }
public int getDefaultCoordX() { public int getDefaultCoordX() {

View File

@ -286,7 +286,7 @@ public class PointerTracker {
mKeyDetector = keyDetector; mKeyDetector = keyDetector;
mKeyboard = keyDetector.getKeyboard(); mKeyboard = keyDetector.getKeyboard();
mKeys = mKeyboard.getKeys(); mKeys = mKeyboard.getKeys();
final int keyQuarterWidth = mKeyboard.getKeyWidth() / 4; final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth; mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
} }

View File

@ -108,8 +108,8 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final Keyboard keyboard = getKeyboard(); final Keyboard keyboard = getKeyboard();
if (keyboard != null) { if (keyboard != null) {
final int width = keyboard.getMinWidth() + getPaddingLeft() + getPaddingRight(); final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
final int height = keyboard.getKeyboardHeight() + getPaddingTop() + getPaddingBottom(); final int height = keyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
setMeasuredDimension(width, height); setMeasuredDimension(width, height);
} else { } else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@ -170,9 +170,9 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
final int miniKeyboardLeft = pointX - miniKeyboard.getDefaultCoordX() final int miniKeyboardLeft = pointX - miniKeyboard.getDefaultCoordX()
+ parentKeyboardView.getPaddingLeft(); + parentKeyboardView.getPaddingLeft();
final int x = Math.max(0, Math.min(miniKeyboardLeft, final int x = Math.max(0, Math.min(miniKeyboardLeft,
parentKeyboardView.getWidth() - miniKeyboard.getMinWidth())) parentKeyboardView.getWidth() - miniKeyboard.mOccupiedWidth))
- container.getPaddingLeft() + mCoordinates[0]; - container.getPaddingLeft() + mCoordinates[0];
final int y = pointY - parentKeyboard.getVerticalGap() final int y = pointY - parentKeyboard.mVerticalGap
- (container.getMeasuredHeight() - container.getPaddingBottom()) - (container.getMeasuredHeight() - container.getPaddingBottom())
+ parentKeyboardView.getPaddingTop() + mCoordinates[1]; + parentKeyboardView.getPaddingTop() + mCoordinates[1];

View File

@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardId;
import java.util.ArrayList; import java.util.ArrayList;
@ -31,8 +32,8 @@ import java.util.Set;
public class KeyboardParams { public class KeyboardParams {
public KeyboardId mId; public KeyboardId mId;
public int mTotalHeight; public int mOccupiedHeight;
public int mTotalWidth; public int mOccupiedWidth;
public int mHeight; public int mHeight;
public int mWidth; public int mWidth;
@ -61,40 +62,34 @@ public class KeyboardParams {
public final Map<Key, Drawable> mUnshiftedIcons = new HashMap<Key, Drawable>(); public final Map<Key, Drawable> mUnshiftedIcons = new HashMap<Key, Drawable>();
public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
public void addShiftKey(Key key) { public int mMostCommonKeyWidth = 0;
if (key == null) return;
public void onAddKey(Key key) {
mKeys.add(key);
updateHistogram(key);
if (key.mCode == Keyboard.CODE_SHIFT) {
mShiftKeys.add(key); mShiftKeys.add(key);
if (key.mSticky) { if (key.mSticky) {
mShiftLockKeys.add(key); mShiftLockKeys.add(key);
} }
} }
}
public void addShiftedIcon(Key key, Drawable icon) { public void addShiftedIcon(Key key, Drawable icon) {
if (key == null) return;
mUnshiftedIcons.put(key, key.getIcon()); mUnshiftedIcons.put(key, key.getIcon());
mShiftedIcons.put(key, icon); mShiftedIcons.put(key, icon);
} }
/** private int mMaxCount = 0;
* Compute the most common key width in order to use it as proximity key detection threshold. private final Map<Integer, Integer> mHistogram = new HashMap<Integer, Integer>();
*
* @return The most common key width in the keyboard private void updateHistogram(Key key) {
*/
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.mHorizontalGap; final Integer width = key.mWidth + key.mHorizontalGap;
Integer count = histogram.get(width); final int count = (mHistogram.containsKey(width) ? mHistogram.get(width) : 0) + 1;
if (count == null) mHistogram.put(width, count);
count = 0; if (count > mMaxCount) {
histogram.put(width, ++count); mMaxCount = count;
if (count > maxCount) { mMostCommonKeyWidth = width;
maxCount = count;
mostCommonWidth = width;
} }
} }
return mostCommonWidth;
}
} }

View File

@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.content.res.XmlResourceParser; import android.content.res.XmlResourceParser;
import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.TypedValue; import android.util.TypedValue;
import android.util.Xml; import android.util.Xml;
@ -107,7 +108,7 @@ import java.util.List;
* </pre> * </pre>
*/ */
public class KeyboardParser { public class KeyboardParser<KP extends KeyboardParams> {
private static final String TAG = KeyboardParser.class.getSimpleName(); private static final String TAG = KeyboardParser.class.getSimpleName();
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
@ -123,40 +124,52 @@ public class KeyboardParser {
private static final String TAG_DEFAULT = "default"; private static final String TAG_DEFAULT = "default";
public static final String TAG_KEY_STYLE = "key-style"; public static final String TAG_KEY_STYLE = "key-style";
private final Keyboard mKeyboard; protected final KP mParams;
private final Context mContext; protected final Context mContext;
private final Resources mResources; protected final Resources mResources;
private final DisplayMetrics mDisplayMetrics;
private int mKeyboardTopPadding;
private int mKeyboardBottomPadding;
private int mHorizontalEdgesPadding;
private int mCurrentX = 0; private int mCurrentX = 0;
private int mCurrentY = 0; private int mCurrentY = 0;
private int mMaxRowWidth = 0;
private int mTotalHeight = 0;
private Row mCurrentRow = null; private Row mCurrentRow = null;
private boolean mLeftEdge; private boolean mLeftEdge;
private Key mRightEdgeKey = null; private Key mRightEdgeKey = null;
private final KeyStyles mKeyStyles = new KeyStyles(); private final KeyStyles mKeyStyles = new KeyStyles();
public KeyboardParser(Keyboard keyboard, Context context) { public KeyboardParser(Context context, KP params) {
mKeyboard = keyboard;
mContext = context; mContext = context;
final Resources res = context.getResources(); final Resources res = context.getResources();
mResources = res; mResources = res;
mHorizontalEdgesPadding = (int)res.getDimension(R.dimen.keyboard_horizontal_edges_padding); mDisplayMetrics = res.getDisplayMetrics();
mParams = params;
mParams.mHorizontalEdgesPadding = (int)res.getDimension(
R.dimen.keyboard_horizontal_edges_padding);
mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
} }
public int getMaxRowWidth() { public KeyboardParser<KP> load(KeyboardId id) {
return mMaxRowWidth; mParams.mId = id;
try {
parseKeyboard(id.getXmlId());
} catch (XmlPullParserException e) {
Log.w(TAG, "keyboard XML parse error: " + e);
throw new IllegalArgumentException(e);
} catch (IOException e) {
Log.w(TAG, "keyboard XML parse error: " + e);
throw new RuntimeException(e);
}
return this;
} }
public int getTotalHeight() { public Keyboard build() {
return mTotalHeight; return new Keyboard(mParams);
} }
public void parseKeyboard(int resId) throws XmlPullParserException, IOException { private void parseKeyboard(int resId) throws XmlPullParserException, IOException {
if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mKeyboard.mId)); if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId));
final XmlResourceParser parser = mResources.getXml(resId); final XmlResourceParser parser = mResources.getXml(resId);
int event; int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@ -165,7 +178,7 @@ public class KeyboardParser {
if (TAG_KEYBOARD.equals(tag)) { if (TAG_KEYBOARD.equals(tag)) {
parseKeyboardAttributes(parser); parseKeyboardAttributes(parser);
startKeyboard(); startKeyboard();
parseKeyboardContent(parser, mKeyboard.getKeys()); parseKeyboardContent(parser, false);
break; break;
} else { } else {
throw new IllegalStartTag(parser, TAG_KEYBOARD); throw new IllegalStartTag(parser, TAG_KEYBOARD);
@ -196,15 +209,14 @@ public class KeyboardParser {
} }
private void parseKeyboardAttributes(XmlResourceParser parser) { private void parseKeyboardAttributes(XmlResourceParser parser) {
final Keyboard keyboard = mKeyboard; final int displayWidth = mDisplayMetrics.widthPixels;
final int displayWidth = keyboard.getDisplayWidth();
final TypedArray keyboardAttr = mContext.obtainStyledAttributes( final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle, Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
R.style.Keyboard); R.style.Keyboard);
final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard_Key); R.styleable.Keyboard_Key);
try { try {
final int displayHeight = keyboard.getDisplayHeight(); final int displayHeight = mDisplayMetrics.heightPixels;
final int keyboardHeight = (int)keyboardAttr.getDimension( final int keyboardHeight = (int)keyboardAttr.getDimension(
R.styleable.Keyboard_keyboardHeight, displayHeight / 2); R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
final int maxKeyboardHeight = getDimensionOrFraction(keyboardAttr, final int maxKeyboardHeight = getDimensionOrFraction(keyboardAttr,
@ -219,37 +231,41 @@ public class KeyboardParser {
} }
// Keyboard height will not exceed maxKeyboardHeight and will not be less than // Keyboard height will not exceed maxKeyboardHeight and will not be less than
// minKeyboardHeight. // minKeyboardHeight.
final int height = Math.max( mParams.mOccupiedHeight = Math.max(
Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight); Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
mParams.mOccupiedWidth = mParams.mId.mWidth;
mParams.mTopPadding = getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_keyboardTopPadding, mParams.mOccupiedHeight, 0);
mParams.mBottomPadding = getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_keyboardBottomPadding, mParams.mOccupiedHeight, 0);
keyboard.setKeyboardHeight(height); final int height = mParams.mOccupiedHeight;
keyboard.setRtlKeyboard(keyboardAttr.getBoolean( final int width = mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding * 2
R.styleable.Keyboard_isRtlKeyboard, false)); - mParams.mHorizontalCenterPadding;
keyboard.setKeyWidth(getDimensionOrFraction(keyboardAttr, mParams.mHeight = height;
R.styleable.Keyboard_keyWidth, displayWidth, displayWidth / 10)); mParams.mWidth = width;
keyboard.setRowHeight(getDimensionOrFraction(keyboardAttr, mParams.mDefaultKeyWidth = getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_rowHeight, height, 50)); R.styleable.Keyboard_keyWidth, width, width / 10);
keyboard.setHorizontalGap(getDimensionOrFraction(keyboardAttr, mParams.mDefaultRowHeight = getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_horizontalGap, displayWidth, 0)); R.styleable.Keyboard_rowHeight, height, height / 4);
keyboard.setVerticalGap(getDimensionOrFraction(keyboardAttr, mParams.mHorizontalGap = getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_verticalGap, height, 0)); R.styleable.Keyboard_horizontalGap, width, 0);
keyboard.setPopupKeyboardResId(keyboardAttr.getResourceId( mParams.mVerticalGap = getDimensionOrFraction(keyboardAttr,
R.styleable.Keyboard_popupKeyboardTemplate, 0)); R.styleable.Keyboard_verticalGap, height, 0);
keyboard.setMaxPopupKeyboardColumn(keyAttr.getInt(
R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5));
mKeyboard.mIconsSet.loadIcons(keyboardAttr); mParams.mIsRtlKeyboard = keyboardAttr.getBoolean(R.styleable.Keyboard_isRtlKeyboard, false);
mKeyboardTopPadding = getDimensionOrFraction(keyboardAttr, mParams.mPopupKeyboardResId = keyboardAttr.getResourceId(
R.styleable.Keyboard_keyboardTopPadding, height, 0); R.styleable.Keyboard_popupKeyboardTemplate, 0);
mKeyboardBottomPadding = getDimensionOrFraction(keyboardAttr, mParams.mMaxPopupColumn = keyAttr.getInt(R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5);
R.styleable.Keyboard_keyboardBottomPadding, height, 0);
mParams.mIconsSet.loadIcons(keyboardAttr);
} finally { } finally {
keyAttr.recycle(); keyAttr.recycle();
keyboardAttr.recycle(); keyboardAttr.recycle();
} }
} }
private void parseKeyboardContent(XmlResourceParser parser, List<Key> keys) private void parseKeyboardContent(XmlResourceParser parser, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
int event; int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@ -258,22 +274,22 @@ public class KeyboardParser {
if (TAG_ROW.equals(tag)) { if (TAG_ROW.equals(tag)) {
Row row = parseRowAttributes(parser); Row row = parseRowAttributes(parser);
if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW)); if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW));
if (keys != null) if (!skip)
startRow(row); startRow(row);
parseRowContent(parser, row, keys); parseRowContent(parser, row, skip);
} else if (TAG_INCLUDE.equals(tag)) { } else if (TAG_INCLUDE.equals(tag)) {
parseIncludeKeyboardContent(parser, keys); parseIncludeKeyboardContent(parser, skip);
} else if (TAG_SWITCH.equals(tag)) { } else if (TAG_SWITCH.equals(tag)) {
parseSwitchKeyboardContent(parser, keys); parseSwitchKeyboardContent(parser, skip);
} else if (TAG_KEY_STYLE.equals(tag)) { } else if (TAG_KEY_STYLE.equals(tag)) {
parseKeyStyle(parser, keys); parseKeyStyle(parser, skip);
} else { } else {
throw new IllegalStartTag(parser, TAG_ROW); throw new IllegalStartTag(parser, TAG_ROW);
} }
} else if (event == XmlPullParser.END_TAG) { } else if (event == XmlPullParser.END_TAG) {
final String tag = parser.getName(); final String tag = parser.getName();
if (TAG_KEYBOARD.equals(tag)) { if (TAG_KEYBOARD.equals(tag)) {
endKeyboard(mKeyboard.getVerticalGap()); endKeyboard();
break; break;
} else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
|| TAG_MERGE.equals(tag)) { || TAG_MERGE.equals(tag)) {
@ -296,28 +312,28 @@ public class KeyboardParser {
throw new IllegalAttribute(parser, "horizontalGap"); throw new IllegalAttribute(parser, "horizontalGap");
if (a.hasValue(R.styleable.Keyboard_verticalGap)) if (a.hasValue(R.styleable.Keyboard_verticalGap))
throw new IllegalAttribute(parser, "verticalGap"); throw new IllegalAttribute(parser, "verticalGap");
return new Row(mResources, mKeyboard, parser); return new Row(mResources, mParams, parser);
} finally { } finally {
a.recycle(); a.recycle();
} }
} }
private void parseRowContent(XmlResourceParser parser, Row row, List<Key> keys) private void parseRowContent(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
int event; int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (event == XmlPullParser.START_TAG) { if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName(); final String tag = parser.getName();
if (TAG_KEY.equals(tag)) { if (TAG_KEY.equals(tag)) {
parseKey(parser, row, keys); parseKey(parser, row, skip);
} else if (TAG_SPACER.equals(tag)) { } else if (TAG_SPACER.equals(tag)) {
parseSpacer(parser, row, keys); parseSpacer(parser, row, skip);
} else if (TAG_INCLUDE.equals(tag)) { } else if (TAG_INCLUDE.equals(tag)) {
parseIncludeRowContent(parser, row, keys); parseIncludeRowContent(parser, row, skip);
} else if (TAG_SWITCH.equals(tag)) { } else if (TAG_SWITCH.equals(tag)) {
parseSwitchRowContent(parser, row, keys); parseSwitchRowContent(parser, row, skip);
} else if (TAG_KEY_STYLE.equals(tag)) { } else if (TAG_KEY_STYLE.equals(tag)) {
parseKeyStyle(parser, keys); parseKeyStyle(parser, skip);
} else { } else {
throw new IllegalStartTag(parser, TAG_KEY); throw new IllegalStartTag(parser, TAG_KEY);
} }
@ -325,7 +341,7 @@ public class KeyboardParser {
final String tag = parser.getName(); final String tag = parser.getName();
if (TAG_ROW.equals(tag)) { if (TAG_ROW.equals(tag)) {
if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW)); if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW));
if (keys != null) if (!skip)
endRow(); endRow();
break; break;
} else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
@ -341,24 +357,24 @@ public class KeyboardParser {
} }
} }
private void parseKey(XmlResourceParser parser, Row row, List<Key> keys) private void parseKey(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
if (keys == null) { if (skip) {
checkEndTag(TAG_KEY, parser); checkEndTag(TAG_KEY, parser);
} else { } else {
Key key = new Key(mResources, row, mCurrentX, mCurrentY, parser, mKeyStyles); Key key = new Key(mResources, mParams, row, mCurrentX, mCurrentY, parser, mKeyStyles);
if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d popupCharacters=%s />", if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d popupCharacters=%s />",
TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode, TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode,
Arrays.toString(key.mPopupCharacters))); Arrays.toString(key.mPopupCharacters)));
checkEndTag(TAG_KEY, parser); checkEndTag(TAG_KEY, parser);
keys.add(key); mParams.onAddKey(key);
endKey(key); endKey(key);
} }
} }
private void parseSpacer(XmlResourceParser parser, Row row, List<Key> keys) private void parseSpacer(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
if (keys == null) { if (skip) {
checkEndTag(TAG_SPACER, parser); checkEndTag(TAG_SPACER, parser);
} else { } else {
if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER)); if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER));
@ -366,9 +382,9 @@ public class KeyboardParser {
R.styleable.Keyboard); R.styleable.Keyboard);
if (keyboardAttr.hasValue(R.styleable.Keyboard_horizontalGap)) if (keyboardAttr.hasValue(R.styleable.Keyboard_horizontalGap))
throw new IllegalAttribute(parser, "horizontalGap"); throw new IllegalAttribute(parser, "horizontalGap");
final int keyboardWidth = mKeyboard.getDisplayWidth(); final int keyboardWidth = mParams.mWidth;
final int keyWidth = getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_keyWidth, final int keyWidth = getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_keyWidth,
keyboardWidth, row.mDefaultWidth); keyboardWidth, row.mDefaultKeyWidth);
keyboardAttr.recycle(); keyboardAttr.recycle();
final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@ -385,19 +401,19 @@ public class KeyboardParser {
} }
} }
private void parseIncludeKeyboardContent(XmlResourceParser parser, List<Key> keys) private void parseIncludeKeyboardContent(XmlResourceParser parser, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
parseIncludeInternal(parser, null, keys); parseIncludeInternal(parser, null, skip);
} }
private void parseIncludeRowContent(XmlResourceParser parser, Row row, List<Key> keys) private void parseIncludeRowContent(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
parseIncludeInternal(parser, row, keys); parseIncludeInternal(parser, row, skip);
} }
private void parseIncludeInternal(XmlResourceParser parser, Row row, List<Key> keys) private void parseIncludeInternal(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
if (keys == null) { if (skip) {
checkEndTag(TAG_INCLUDE, parser); checkEndTag(TAG_INCLUDE, parser);
} else { } else {
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@ -411,11 +427,11 @@ public class KeyboardParser {
throw new ParseException("No keyboardLayout attribute in <include/>", parser); throw new ParseException("No keyboardLayout attribute in <include/>", parser);
if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />", if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />",
TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout))); TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout)));
parseMerge(mResources.getLayout(keyboardLayout), row, keys); parseMerge(mResources.getLayout(keyboardLayout), row, skip);
} }
} }
private void parseMerge(XmlResourceParser parser, Row row, List<Key> keys) private void parseMerge(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
int event; int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@ -423,9 +439,9 @@ public class KeyboardParser {
final String tag = parser.getName(); final String tag = parser.getName();
if (TAG_MERGE.equals(tag)) { if (TAG_MERGE.equals(tag)) {
if (row == null) { if (row == null) {
parseKeyboardContent(parser, keys); parseKeyboardContent(parser, skip);
} else { } else {
parseRowContent(parser, row, keys); parseRowContent(parser, row, skip);
} }
break; break;
} else { } else {
@ -436,28 +452,28 @@ public class KeyboardParser {
} }
} }
private void parseSwitchKeyboardContent(XmlResourceParser parser, List<Key> keys) private void parseSwitchKeyboardContent(XmlResourceParser parser, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
parseSwitchInternal(parser, null, keys); parseSwitchInternal(parser, null, skip);
} }
private void parseSwitchRowContent(XmlResourceParser parser, Row row, List<Key> keys) private void parseSwitchRowContent(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
parseSwitchInternal(parser, row, keys); parseSwitchInternal(parser, row, skip);
} }
private void parseSwitchInternal(XmlResourceParser parser, Row row, List<Key> keys) private void parseSwitchInternal(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mKeyboard.mId)); if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId));
boolean selected = false; boolean selected = false;
int event; int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (event == XmlPullParser.START_TAG) { if (event == XmlPullParser.START_TAG) {
final String tag = parser.getName(); final String tag = parser.getName();
if (TAG_CASE.equals(tag)) { if (TAG_CASE.equals(tag)) {
selected |= parseCase(parser, row, selected ? null : keys); selected |= parseCase(parser, row, selected ? true : skip);
} else if (TAG_DEFAULT.equals(tag)) { } else if (TAG_DEFAULT.equals(tag)) {
selected |= parseDefault(parser, row, selected ? null : keys); selected |= parseDefault(parser, row, selected ? true : skip);
} else { } else {
throw new IllegalStartTag(parser, TAG_KEY); throw new IllegalStartTag(parser, TAG_KEY);
} }
@ -473,21 +489,21 @@ public class KeyboardParser {
} }
} }
private boolean parseCase(XmlResourceParser parser, Row row, List<Key> keys) private boolean parseCase(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
final boolean selected = parseCaseCondition(parser); final boolean selected = parseCaseCondition(parser);
if (row == null) { if (row == null) {
// Processing Rows. // Processing Rows.
parseKeyboardContent(parser, selected ? keys : null); parseKeyboardContent(parser, selected ? skip : true);
} else { } else {
// Processing Keys. // Processing Keys.
parseRowContent(parser, row, selected ? keys : null); parseRowContent(parser, row, selected ? skip : true);
} }
return selected; return selected;
} }
private boolean parseCaseCondition(XmlResourceParser parser) { private boolean parseCaseCondition(XmlResourceParser parser) {
final KeyboardId id = mKeyboard.mId; final KeyboardId id = mParams.mId;
if (id == null) if (id == null)
return true; return true;
@ -596,18 +612,18 @@ public class KeyboardParser {
return false; return false;
} }
private boolean parseDefault(XmlResourceParser parser, Row row, List<Key> keys) private boolean parseDefault(XmlResourceParser parser, Row row, boolean skip)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT)); if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
if (row == null) { if (row == null) {
parseKeyboardContent(parser, keys); parseKeyboardContent(parser, skip);
} else { } else {
parseRowContent(parser, row, keys); parseRowContent(parser, row, skip);
} }
return true; return true;
} }
private void parseKeyStyle(XmlResourceParser parser, List<Key> keys) { private void parseKeyStyle(XmlResourceParser parser, boolean skip) {
TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard_KeyStyle); R.styleable.Keyboard_KeyStyle);
TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser), TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@ -616,7 +632,7 @@ public class KeyboardParser {
if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
throw new ParseException("<" + TAG_KEY_STYLE throw new ParseException("<" + TAG_KEY_STYLE
+ "/> needs styleName attribute", parser); + "/> needs styleName attribute", parser);
if (keys != null) if (!skip)
mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser); mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
} finally { } finally {
keyStyleAttr.recycle(); keyStyleAttr.recycle();
@ -632,12 +648,12 @@ public class KeyboardParser {
} }
private void startKeyboard() { private void startKeyboard() {
mCurrentY += mKeyboardTopPadding; mCurrentY += mParams.mTopPadding;
} }
private void startRow(Row row) { private void startRow(Row row) {
mCurrentX = 0; mCurrentX = 0;
setSpacer(mCurrentX, mHorizontalEdgesPadding); setSpacer(mCurrentX, mParams.mHorizontalEdgesPadding);
mCurrentRow = row; mCurrentRow = row;
mLeftEdge = true; mLeftEdge = true;
mRightEdgeKey = null; mRightEdgeKey = null;
@ -650,10 +666,8 @@ public class KeyboardParser {
mRightEdgeKey.addEdgeFlags(Keyboard.EDGE_RIGHT); mRightEdgeKey.addEdgeFlags(Keyboard.EDGE_RIGHT);
mRightEdgeKey = null; mRightEdgeKey = null;
} }
setSpacer(mCurrentX, mHorizontalEdgesPadding); setSpacer(mCurrentX, mParams.mHorizontalEdgesPadding);
if (mCurrentX > mMaxRowWidth) mCurrentY += mCurrentRow.mRowHeight;
mMaxRowWidth = mCurrentX;
mCurrentY += mCurrentRow.mDefaultHeight;
mCurrentRow = null; mCurrentRow = null;
} }
@ -666,9 +680,7 @@ public class KeyboardParser {
mRightEdgeKey = key; mRightEdgeKey = key;
} }
private void endKeyboard(int defaultVerticalGap) { private void endKeyboard() {
mCurrentY += mKeyboardBottomPadding;
mTotalHeight = mCurrentY - defaultVerticalGap;
} }
private void setSpacer(int keyXPos, int width) { private void setSpacer(int keyXPos, int width) {

View File

@ -16,8 +16,6 @@
package com.android.inputmethod.keyboard.internal; package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Rect; import android.graphics.Rect;
@ -27,26 +25,30 @@ import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.MiniKeyboard; import com.android.inputmethod.keyboard.MiniKeyboard;
import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.R;
import java.util.List; public class MiniKeyboardBuilder extends
KeyboardParser<MiniKeyboardBuilder.MiniKeyboardLayoutParams> {
public class MiniKeyboardBuilder {
private final Resources mRes;
private final MiniKeyboard mKeyboard;
private final CharSequence[] mPopupCharacters; private final CharSequence[] mPopupCharacters;
private final MiniKeyboardLayoutParams mParams;
/* package */ static class MiniKeyboardLayoutParams { public static class MiniKeyboardLayoutParams extends KeyboardParams {
public final int mKeyWidth; /* package */ int mTopRowAdjustment;
public final int mRowHeight; public int mNumRows;
/* package */ final int mTopRowAdjustment; public int mNumColumns;
public final int mNumRows; public int mLeftKeys;
public final int mNumColumns; public int mRightKeys; // includes default key.
public final int mLeftKeys;
public final int mRightKeys; // includes default key. public MiniKeyboardLayoutParams() {
public int mTopPadding; super();
}
/* package for test */ MiniKeyboardLayoutParams(int numKeys, int maxColumns, int keyWidth,
int rowHeight, int coordXInParent, int parentKeyboardWidth) {
super();
setParameters(
numKeys, maxColumns, keyWidth, rowHeight, coordXInParent, parentKeyboardWidth);
}
/** /**
* The object holding mini keyboard layout parameters. * Set keyboard parameters of mini keyboard.
* *
* @param numKeys number of keys in this mini keyboard. * @param numKeys number of keys in this mini keyboard.
* @param maxColumns number of maximum columns of this mini keyboard. * @param maxColumns number of maximum columns of this mini keyboard.
@ -54,15 +56,15 @@ public class MiniKeyboardBuilder {
* @param rowHeight mini keyboard row height in pixel, including vertical gap. * @param rowHeight mini keyboard row height in pixel, including vertical gap.
* @param coordXInParent coordinate x of the popup key in parent keyboard. * @param coordXInParent coordinate x of the popup key in parent keyboard.
* @param parentKeyboardWidth parent keyboard width in pixel. * @param parentKeyboardWidth parent keyboard width in pixel.
* parent keyboard.
*/ */
public MiniKeyboardLayoutParams(int numKeys, int maxColumns, int keyWidth, int rowHeight, public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight,
int coordXInParent, int parentKeyboardWidth) { int coordXInParent, int parentKeyboardWidth) {
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);
mKeyWidth = keyWidth; }
mRowHeight = rowHeight; mDefaultKeyWidth = keyWidth;
mDefaultRowHeight = rowHeight;
final int numRows = (numKeys + maxColumns - 1) / maxColumns; final int numRows = (numKeys + maxColumns - 1) / maxColumns;
mNumRows = numRows; mNumRows = numRows;
@ -108,6 +110,9 @@ public class MiniKeyboardBuilder {
} else { } else {
mTopRowAdjustment = -1; mTopRowAdjustment = -1;
} }
mWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth;
mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
} }
// Return key position according to column count (0 is default). // Return key position according to column count (0 is default).
@ -160,19 +165,19 @@ public class MiniKeyboardBuilder {
} }
public int getDefaultKeyCoordX() { public int getDefaultKeyCoordX() {
return mLeftKeys * mKeyWidth; return mLeftKeys * mDefaultKeyWidth;
} }
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) * mDefaultKeyWidth + getDefaultKeyCoordX();
if (isTopRow(row)) { if (isTopRow(row)) {
return x + mTopRowAdjustment * (mKeyWidth / 2); return x + mTopRowAdjustment * (mDefaultKeyWidth / 2);
} }
return x; return x;
} }
public int getY(int row) { public int getY(int row) {
return (mNumRows - 1 - row) * mRowHeight + mTopPadding; return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
} }
public int getRowFlags(int row) { public int getRowFlags(int row) {
@ -185,42 +190,32 @@ public class MiniKeyboardBuilder {
private boolean isTopRow(int rowCount) { private boolean isTopRow(int rowCount) {
return rowCount == mNumRows - 1; return rowCount == mNumRows - 1;
} }
public void setTopPadding (int topPadding) {
mTopPadding = topPadding;
} }
public int getKeyboardHeight() { public MiniKeyboardBuilder(KeyboardView view, int xmlId, Key parentKey,
return mNumRows * mRowHeight + mTopPadding;
}
public int getKeyboardWidth() {
return mNumColumns * mKeyWidth;
}
}
public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey,
Keyboard parentKeyboard) { Keyboard parentKeyboard) {
final Context context = view.getContext(); super(view.getContext(), new MiniKeyboardLayoutParams());
mRes = context.getResources(); load(parentKeyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId));
final MiniKeyboard keyboard = new MiniKeyboard(
context, layoutTemplateResId, parentKeyboard); // HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal
mKeyboard = keyboard; // and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are
// needed to keep having the same horizontal and vertical key spacing.
mParams.mHorizontalGap = 0;
mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
// TODO: When we have correctly padded key background 9-patch drawables for mini keyboard,
// revert the above hacks and uncomment the following lines.
//mParams.mHorizontalGap = parentKeyboard.mHorizontalGap;
//mParams.mVerticalGap = parentKeyboard.mVerticalGap;
mParams.mIsRtlKeyboard = parentKeyboard.mIsRtlKeyboard;
mPopupCharacters = parentKey.mPopupCharacters; mPopupCharacters = parentKey.mPopupCharacters;
final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth()); final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, mParams.mDefaultKeyWidth);
final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams( mParams.setParameters(
mPopupCharacters.length, parentKey.mMaxPopupColumn, mPopupCharacters.length, parentKey.mMaxPopupColumn,
keyWidth, parentKeyboard.getRowHeight(), keyWidth, parentKeyboard.mDefaultRowHeight,
parentKey.mX + (parentKey.mWidth + parentKey.mHorizontalGap) / 2 - keyWidth / 2, parentKey.mX + (mParams.mDefaultKeyWidth - keyWidth) / 2,
view.getMeasuredWidth()); view.getMeasuredWidth());
params.setTopPadding(keyboard.getVerticalGap());
mParams = params;
keyboard.setRowHeight(params.mRowHeight);
keyboard.setKeyboardHeight(params.getKeyboardHeight());
keyboard.setMinWidth(params.getKeyboardWidth());
keyboard.setDefaultCoordX(params.getDefaultKeyCoordX() + params.mKeyWidth / 2);
} }
private static int getMaxKeyWidth(KeyboardView view, CharSequence[] popupCharacters, private static int getMaxKeyWidth(KeyboardView view, CharSequence[] popupCharacters,
@ -249,17 +244,16 @@ public class MiniKeyboardBuilder {
return Math.max(minKeyWidth, maxWidth + horizontalPadding); return Math.max(minKeyWidth, maxWidth + horizontalPadding);
} }
@Override
public MiniKeyboard build() { public MiniKeyboard build() {
final MiniKeyboard keyboard = mKeyboard;
final List<Key> keys = keyboard.getKeys();
final MiniKeyboardLayoutParams params = mParams; final MiniKeyboardLayoutParams params = mParams;
for (int n = 0; n < mPopupCharacters.length; n++) { for (int n = 0; n < mPopupCharacters.length; n++) {
final CharSequence label = mPopupCharacters[n]; final CharSequence label = mPopupCharacters[n];
final int row = n / params.mNumColumns; final int row = n / params.mNumColumns;
final Key key = new Key(mRes, keyboard, label, params.getX(n, row), params.getY(row), final Key key = new Key(mResources, params, label, params.getX(n, row), params.getY(row),
params.mKeyWidth, params.mRowHeight, params.getRowFlags(row)); params.mDefaultKeyWidth, params.mDefaultRowHeight, params.getRowFlags(row));
keys.add(key); params.onAddKey(key);
} }
return keyboard; return new MiniKeyboard(params);
} }
} }

View File

@ -31,26 +31,19 @@ import com.android.inputmethod.latin.R;
*/ */
public class Row { public class Row {
/** Default width of a key in this row. */ /** Default width of a key in this row. */
public final int mDefaultWidth; public final int mDefaultKeyWidth;
/** Default height of a key in this row. */ /** Default height of a key in this row. */
public final int mDefaultHeight; public final int mRowHeight;
private final Keyboard mKeyboard; public Row(Resources res, KeyboardParams params, XmlResourceParser parser) {
final int keyboardWidth = params.mWidth;
public Row(Resources res, Keyboard keyboard, XmlResourceParser parser) { final int keyboardHeight = params.mHeight;
this.mKeyboard = keyboard;
final int keyboardWidth = keyboard.getDisplayWidth();
final int keyboardHeight = keyboard.getKeyboardHeight();
TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser), TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard); R.styleable.Keyboard);
mDefaultWidth = KeyboardParser.getDimensionOrFraction(a, mDefaultKeyWidth = KeyboardParser.getDimensionOrFraction(a,
R.styleable.Keyboard_keyWidth, keyboardWidth, keyboard.getKeyWidth()); R.styleable.Keyboard_keyWidth, keyboardWidth, params.mDefaultKeyWidth);
mDefaultHeight = KeyboardParser.getDimensionOrFraction(a, mRowHeight = KeyboardParser.getDimensionOrFraction(a,
R.styleable.Keyboard_rowHeight, keyboardHeight, keyboard.getRowHeight()); R.styleable.Keyboard_rowHeight, keyboardHeight, params.mDefaultRowHeight);
a.recycle(); a.recycle();
} }
public Keyboard getKeyboard() {
return mKeyboard;
}
} }

View File

@ -1685,7 +1685,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
final int rawPrimaryCode = suggestion.charAt(0); final int rawPrimaryCode = suggestion.charAt(0);
// Maybe apply the "bidi mirrored" conversions for parentheses // Maybe apply the "bidi mirrored" conversions for parentheses
final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard(); final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard();
final int primaryCode = keyboard.isRtlKeyboard() final int primaryCode = keyboard.mIsRtlKeyboard
? Key.getRtlParenthesisCode(rawPrimaryCode) : rawPrimaryCode; ? Key.getRtlParenthesisCode(rawPrimaryCode) : rawPrimaryCode;
final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : ""; final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : "";

View File

@ -36,7 +36,7 @@ public class SuggestHelper {
// Use null as the locale for Suggest so as to force it to use the internal dictionary // Use null as the locale for Suggest so as to force it to use the internal dictionary
// (and not try to find a dictionary provider for a specified locale) // (and not try to find a dictionary provider for a specified locale)
mSuggest = new Suggest(context, dictionaryId, null); mSuggest = new Suggest(context, dictionaryId, null);
mKeyboard = new LatinKeyboard(context, keyboardId, keyboardId.mWidth); mKeyboard = new LatinKeyboard.Builder(context).load(keyboardId).build();
mKeyDetector = new KeyDetector(0); mKeyDetector = new KeyDetector(0);
init(); init();
} }
@ -44,7 +44,7 @@ public class SuggestHelper {
protected SuggestHelper(Context context, File dictionaryPath, long startOffset, long length, protected SuggestHelper(Context context, File dictionaryPath, long startOffset, long length,
KeyboardId keyboardId) { KeyboardId keyboardId) {
mSuggest = new Suggest(context, dictionaryPath, startOffset, length, null); mSuggest = new Suggest(context, dictionaryPath, startOffset, length, null);
mKeyboard = new LatinKeyboard(context, keyboardId, keyboardId.mWidth); mKeyboard = new LatinKeyboard.Builder(context).load(keyboardId).build();
mKeyDetector = new KeyDetector(0); mKeyDetector = new KeyDetector(0);
init(); init();
} }
@ -54,7 +54,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(mKeyboard.getMostCommonKeyWidth()); mKeyDetector.setProximityThreshold(mKeyboard.mMostCommonKeyWidth);
} }
public void setCorrectionMode(int correctionMode) { public void setCorrectionMode(int correctionMode) {