am 35ff9454: Separate inner classes of keyboard package out under internal package
* commit '35ff94547c16c84c5b6fafdae0b4a683be782b97': Separate inner classes of keyboard package out under internal packagemain
commit
8b782838fe
|
@ -32,9 +32,11 @@ import android.util.Log;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.internal.KeySpecParser;
|
import com.android.inputmethod.keyboard.internal.KeySpecParser;
|
||||||
import com.android.inputmethod.keyboard.internal.KeySpecParser.MoreKeySpec;
|
import com.android.inputmethod.keyboard.internal.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.KeyboardRow;
|
||||||
|
import com.android.inputmethod.keyboard.internal.MoreKeySpec;
|
||||||
import com.android.inputmethod.latin.R;
|
import com.android.inputmethod.latin.R;
|
||||||
import com.android.inputmethod.latin.ResourceUtils;
|
import com.android.inputmethod.latin.ResourceUtils;
|
||||||
import com.android.inputmethod.latin.StringUtils;
|
import com.android.inputmethod.latin.StringUtils;
|
||||||
|
@ -166,8 +168,8 @@ public class Key {
|
||||||
/**
|
/**
|
||||||
* This constructor is being used only for keys in more keys keyboard.
|
* This constructor is being used only for keys in more keys keyboard.
|
||||||
*/
|
*/
|
||||||
public Key(Keyboard.Params params, MoreKeySpec moreKeySpec, int x, int y, int width, int height,
|
public Key(final KeyboardParams params, final MoreKeySpec moreKeySpec, final int x, final int y,
|
||||||
int labelFlags) {
|
final int width, final int height, final int labelFlags) {
|
||||||
this(params, moreKeySpec.mLabel, null, moreKeySpec.mIconId, moreKeySpec.mCode,
|
this(params, moreKeySpec.mLabel, null, moreKeySpec.mIconId, moreKeySpec.mCode,
|
||||||
moreKeySpec.mOutputText, x, y, width, height, labelFlags);
|
moreKeySpec.mOutputText, x, y, width, height, labelFlags);
|
||||||
}
|
}
|
||||||
|
@ -175,8 +177,9 @@ public class Key {
|
||||||
/**
|
/**
|
||||||
* This constructor is being used only for key in popup suggestions pane.
|
* This constructor is being used only for key in popup suggestions pane.
|
||||||
*/
|
*/
|
||||||
public Key(Keyboard.Params params, String label, String hintLabel, int iconId,
|
public Key(final KeyboardParams params, final String label, final String hintLabel,
|
||||||
int code, String outputText, int x, int y, int width, int height, int labelFlags) {
|
final int iconId, final int code, final String outputText, final int x, final int y,
|
||||||
|
final int width, final int height, final int labelFlags) {
|
||||||
mHeight = height - params.mVerticalGap;
|
mHeight = height - params.mVerticalGap;
|
||||||
mWidth = width - params.mHorizontalGap;
|
mWidth = width - params.mHorizontalGap;
|
||||||
mHintLabel = hintLabel;
|
mHintLabel = hintLabel;
|
||||||
|
@ -213,8 +216,8 @@ public class Key {
|
||||||
* @param parser the XML parser containing the attributes for this key
|
* @param parser the XML parser containing the attributes for this key
|
||||||
* @throws XmlPullParserException
|
* @throws XmlPullParserException
|
||||||
*/
|
*/
|
||||||
public Key(Resources res, Keyboard.Params params, Keyboard.Builder.Row row,
|
public Key(final Resources res, final KeyboardParams params, final KeyboardRow row,
|
||||||
XmlPullParser parser) throws XmlPullParserException {
|
final XmlPullParser parser) throws XmlPullParserException {
|
||||||
final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
|
final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
|
||||||
final int keyHeight = row.mRowHeight;
|
final int keyHeight = row.mRowHeight;
|
||||||
mHeight = keyHeight - params.mVerticalGap;
|
mHeight = keyHeight - params.mVerticalGap;
|
||||||
|
@ -364,7 +367,7 @@ public class Key {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean needsToUpperCase(int labelFlags, int keyboardElementId) {
|
private static boolean needsToUpperCase(final int labelFlags, final int keyboardElementId) {
|
||||||
if ((labelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0) return false;
|
if ((labelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0) return false;
|
||||||
switch (keyboardElementId) {
|
switch (keyboardElementId) {
|
||||||
case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
|
case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
|
||||||
|
@ -377,7 +380,7 @@ public class Key {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int computeHashCode(Key key) {
|
private static int computeHashCode(final Key key) {
|
||||||
return Arrays.hashCode(new Object[] {
|
return Arrays.hashCode(new Object[] {
|
||||||
key.mX,
|
key.mX,
|
||||||
key.mY,
|
key.mY,
|
||||||
|
@ -404,7 +407,7 @@ public class Key {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean equals(Key o) {
|
private boolean equals(final Key o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
return o.mX == mX
|
return o.mX == mX
|
||||||
&& o.mY == mY
|
&& o.mY == mY
|
||||||
|
@ -427,7 +430,7 @@ public class Key {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(final Object o) {
|
||||||
return o instanceof Key && equals((Key)o);
|
return o instanceof Key && equals((Key)o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +447,7 @@ public class Key {
|
||||||
KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType));
|
KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String backgroundName(int backgroundType) {
|
private static String backgroundName(final int backgroundType) {
|
||||||
switch (backgroundType) {
|
switch (backgroundType) {
|
||||||
case BACKGROUND_TYPE_NORMAL: return "normal";
|
case BACKGROUND_TYPE_NORMAL: return "normal";
|
||||||
case BACKGROUND_TYPE_FUNCTIONAL: return "functional";
|
case BACKGROUND_TYPE_FUNCTIONAL: return "functional";
|
||||||
|
@ -455,19 +458,19 @@ public class Key {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markAsLeftEdge(Keyboard.Params params) {
|
public void markAsLeftEdge(final KeyboardParams params) {
|
||||||
mHitBox.left = params.mHorizontalEdgesPadding;
|
mHitBox.left = params.mHorizontalEdgesPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markAsRightEdge(Keyboard.Params params) {
|
public void markAsRightEdge(final KeyboardParams params) {
|
||||||
mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding;
|
mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markAsTopEdge(Keyboard.Params params) {
|
public void markAsTopEdge(final KeyboardParams params) {
|
||||||
mHitBox.top = params.mTopPadding;
|
mHitBox.top = params.mTopPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markAsBottomEdge(Keyboard.Params params) {
|
public void markAsBottomEdge(final KeyboardParams params) {
|
||||||
mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding;
|
mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +504,7 @@ public class Key {
|
||||||
&& (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0;
|
&& (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Typeface selectTypeface(Typeface defaultTypeface) {
|
public Typeface selectTypeface(final Typeface defaultTypeface) {
|
||||||
// TODO: Handle "bold" here too?
|
// TODO: Handle "bold" here too?
|
||||||
if ((mLabelFlags & LABEL_FLAGS_FONT_NORMAL) != 0) {
|
if ((mLabelFlags & LABEL_FLAGS_FONT_NORMAL) != 0) {
|
||||||
return Typeface.DEFAULT;
|
return Typeface.DEFAULT;
|
||||||
|
@ -512,8 +515,8 @@ public class Key {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int selectTextSize(int letterSize, int largeLetterSize, int labelSize,
|
public int selectTextSize(final int letterSize, final int largeLetterSize, final int labelSize,
|
||||||
int largeLabelSize, int hintLabelSize) {
|
final int largeLabelSize, final int hintLabelSize) {
|
||||||
switch (mLabelFlags & LABEL_FLAGS_FOLLOW_KEY_TEXT_RATIO_MASK) {
|
switch (mLabelFlags & LABEL_FLAGS_FOLLOW_KEY_TEXT_RATIO_MASK) {
|
||||||
case LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO:
|
case LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO:
|
||||||
return letterSize;
|
return letterSize;
|
||||||
|
@ -606,7 +609,7 @@ public class Key {
|
||||||
return (attrs != null) ? attrs.mAltCode : CODE_UNSPECIFIED;
|
return (attrs != null) ? attrs.mAltCode : CODE_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Drawable getIcon(KeyboardIconsSet iconSet, int alpha) {
|
public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) {
|
||||||
final OptionalAttributes attrs = mOptionalAttributes;
|
final OptionalAttributes attrs = mOptionalAttributes;
|
||||||
final int disabledIconId = (attrs != null) ? attrs.mDisabledIconId : ICON_UNDEFINED;
|
final int disabledIconId = (attrs != null) ? attrs.mDisabledIconId : ICON_UNDEFINED;
|
||||||
final int iconId = mEnabled ? mIconId : disabledIconId;
|
final int iconId = mEnabled ? mIconId : disabledIconId;
|
||||||
|
@ -617,7 +620,7 @@ public class Key {
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Drawable getPreviewIcon(KeyboardIconsSet iconSet) {
|
public Drawable getPreviewIcon(final KeyboardIconsSet iconSet) {
|
||||||
final OptionalAttributes attrs = mOptionalAttributes;
|
final OptionalAttributes attrs = mOptionalAttributes;
|
||||||
final int previewIconId = (attrs != null) ? attrs.mPreviewIconId : ICON_UNDEFINED;
|
final int previewIconId = (attrs != null) ? attrs.mPreviewIconId : ICON_UNDEFINED;
|
||||||
return previewIconId != ICON_UNDEFINED
|
return previewIconId != ICON_UNDEFINED
|
||||||
|
@ -657,7 +660,7 @@ public class Key {
|
||||||
return mEnabled;
|
return mEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
public void setEnabled(final boolean enabled) {
|
||||||
mEnabled = enabled;
|
mEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,9 +670,9 @@ public class Key {
|
||||||
* @param y the y-coordinate of the point
|
* @param y the y-coordinate of the point
|
||||||
* @return whether or not the point falls on the key. If the key is attached to an edge, it
|
* @return whether or not the point falls on the key. If the key is attached to an edge, it
|
||||||
* will assume that all points between the key and the edge are considered to be on the key.
|
* will assume that all points between the key and the edge are considered to be on the key.
|
||||||
* @see #markAsLeftEdge(Keyboard.Params) etc.
|
* @see #markAsLeftEdge(KeyboardParams) etc.
|
||||||
*/
|
*/
|
||||||
public boolean isOnKey(int x, int y) {
|
public boolean isOnKey(final int x, final int y) {
|
||||||
return mHitBox.contains(x, y);
|
return mHitBox.contains(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,7 +682,7 @@ public class Key {
|
||||||
* @param y the y-coordinate of the point
|
* @param y the y-coordinate of the point
|
||||||
* @return the square of the distance of the point from the nearest edge of the key
|
* @return the square of the distance of the point from the nearest edge of the key
|
||||||
*/
|
*/
|
||||||
public int squaredDistanceToEdge(int x, int y) {
|
public int squaredDistanceToEdge(final int x, final int y) {
|
||||||
final int left = mX;
|
final int left = mX;
|
||||||
final int right = left + mWidth;
|
final int right = left + mWidth;
|
||||||
final int top = mY;
|
final int top = mY;
|
||||||
|
@ -761,15 +764,16 @@ public class Key {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Spacer extends Key {
|
public static class Spacer extends Key {
|
||||||
public Spacer(Resources res, Keyboard.Params params, Keyboard.Builder.Row row,
|
public Spacer(final Resources res, final KeyboardParams params, final KeyboardRow row,
|
||||||
XmlPullParser parser) throws XmlPullParserException {
|
final XmlPullParser parser) throws XmlPullParserException {
|
||||||
super(res, params, row, parser);
|
super(res, params, row, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor is being used only for divider in more keys keyboard.
|
* This constructor is being used only for divider in more keys keyboard.
|
||||||
*/
|
*/
|
||||||
protected Spacer(Keyboard.Params params, int x, int y, int width, int height) {
|
protected Spacer(final KeyboardParams params, final int x, final int y, final int width,
|
||||||
|
final int height) {
|
||||||
super(params, null, null, ICON_UNDEFINED, CODE_UNSPECIFIED,
|
super(params, null, null, ICON_UNDEFINED, CODE_UNSPECIFIED,
|
||||||
null, x, y, width, height, 0);
|
null, x, y, width, height, 0);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -35,7 +35,9 @@ import android.view.inputmethod.EditorInfo;
|
||||||
import android.view.inputmethod.InputMethodSubtype;
|
import android.view.inputmethod.InputMethodSubtype;
|
||||||
|
|
||||||
import com.android.inputmethod.compat.EditorInfoCompatUtils;
|
import com.android.inputmethod.compat.EditorInfoCompatUtils;
|
||||||
import com.android.inputmethod.keyboard.KeyboardLayoutSet.Params.ElementParams;
|
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
|
||||||
|
import com.android.inputmethod.keyboard.internal.KeyboardParams;
|
||||||
|
import com.android.inputmethod.keyboard.internal.KeysCache;
|
||||||
import com.android.inputmethod.latin.CollectionUtils;
|
import com.android.inputmethod.latin.CollectionUtils;
|
||||||
import com.android.inputmethod.latin.InputAttributes;
|
import com.android.inputmethod.latin.InputAttributes;
|
||||||
import com.android.inputmethod.latin.InputTypeUtils;
|
import com.android.inputmethod.latin.InputTypeUtils;
|
||||||
|
@ -78,31 +80,19 @@ public class KeyboardLayoutSet {
|
||||||
public static class KeyboardLayoutSetException extends RuntimeException {
|
public static class KeyboardLayoutSetException extends RuntimeException {
|
||||||
public final KeyboardId mKeyboardId;
|
public final KeyboardId mKeyboardId;
|
||||||
|
|
||||||
public KeyboardLayoutSetException(Throwable cause, KeyboardId keyboardId) {
|
public KeyboardLayoutSetException(final Throwable cause, final KeyboardId keyboardId) {
|
||||||
super(cause);
|
super(cause);
|
||||||
mKeyboardId = keyboardId;
|
mKeyboardId = keyboardId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class KeysCache {
|
private static class ElementParams {
|
||||||
private final HashMap<Key, Key> mMap = CollectionUtils.newHashMap();
|
int mKeyboardXmlId;
|
||||||
|
boolean mProximityCharsCorrectionEnabled;
|
||||||
public void clear() {
|
public ElementParams() {}
|
||||||
mMap.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Key get(Key key) {
|
|
||||||
final Key existingKey = mMap.get(key);
|
|
||||||
if (existingKey != null) {
|
|
||||||
// Reuse the existing element that equals to "key" without adding "key" to the map.
|
|
||||||
return existingKey;
|
|
||||||
}
|
|
||||||
mMap.put(key, key);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Params {
|
private static class Params {
|
||||||
String mKeyboardLayoutSetName;
|
String mKeyboardLayoutSetName;
|
||||||
int mMode;
|
int mMode;
|
||||||
EditorInfo mEditorInfo;
|
EditorInfo mEditorInfo;
|
||||||
|
@ -118,11 +108,7 @@ public class KeyboardLayoutSet {
|
||||||
// Sparse array of KeyboardLayoutSet element parameters indexed by element's id.
|
// Sparse array of KeyboardLayoutSet element parameters indexed by element's id.
|
||||||
final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
|
final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
|
||||||
CollectionUtils.newSparseArray();
|
CollectionUtils.newSparseArray();
|
||||||
|
public Params() {}
|
||||||
static class ElementParams {
|
|
||||||
int mKeyboardXmlId;
|
|
||||||
boolean mProximityCharsCorrectionEnabled;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clearKeyboardCache() {
|
public static void clearKeyboardCache() {
|
||||||
|
@ -130,12 +116,12 @@ public class KeyboardLayoutSet {
|
||||||
sKeysCache.clear();
|
sKeysCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeyboardLayoutSet(Context context, Params params) {
|
KeyboardLayoutSet(final Context context, final Params params) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mParams = params;
|
mParams = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Keyboard getKeyboard(int baseKeyboardLayoutSetElementId) {
|
public Keyboard getKeyboard(final int baseKeyboardLayoutSetElementId) {
|
||||||
final int keyboardLayoutSetElementId;
|
final int keyboardLayoutSetElementId;
|
||||||
switch (mParams.mMode) {
|
switch (mParams.mMode) {
|
||||||
case KeyboardId.MODE_PHONE:
|
case KeyboardId.MODE_PHONE:
|
||||||
|
@ -170,12 +156,12 @@ public class KeyboardLayoutSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Keyboard getKeyboard(ElementParams elementParams, final KeyboardId id) {
|
private Keyboard getKeyboard(final ElementParams elementParams, final KeyboardId id) {
|
||||||
final SoftReference<Keyboard> ref = sKeyboardCache.get(id);
|
final SoftReference<Keyboard> ref = sKeyboardCache.get(id);
|
||||||
Keyboard keyboard = (ref == null) ? null : ref.get();
|
Keyboard keyboard = (ref == null) ? null : ref.get();
|
||||||
if (keyboard == null) {
|
if (keyboard == null) {
|
||||||
final Keyboard.Builder<Keyboard.Params> builder =
|
final KeyboardBuilder<KeyboardParams> builder =
|
||||||
new Keyboard.Builder<Keyboard.Params>(mContext, new Keyboard.Params());
|
new KeyboardBuilder<KeyboardParams>(mContext, new KeyboardParams());
|
||||||
if (id.isAlphabetKeyboard()) {
|
if (id.isAlphabetKeyboard()) {
|
||||||
builder.setAutoGenerate(sKeysCache);
|
builder.setAutoGenerate(sKeysCache);
|
||||||
}
|
}
|
||||||
|
@ -202,7 +188,7 @@ public class KeyboardLayoutSet {
|
||||||
// KeyboardLayoutSet element id that is a key in keyboard_set.xml. Also that file specifies
|
// KeyboardLayoutSet element id that is a key in keyboard_set.xml. Also that file specifies
|
||||||
// which XML layout should be used for each keyboard. The KeyboardId is an internal key for
|
// which XML layout should be used for each keyboard. The KeyboardId is an internal key for
|
||||||
// Keyboard object.
|
// Keyboard object.
|
||||||
private KeyboardId getKeyboardId(int keyboardLayoutSetElementId) {
|
private KeyboardId getKeyboardId(final int keyboardLayoutSetElementId) {
|
||||||
final Params params = mParams;
|
final Params params = mParams;
|
||||||
final boolean isSymbols = (keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS
|
final boolean isSymbols = (keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS
|
||||||
|| keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED);
|
|| keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED);
|
||||||
|
@ -225,7 +211,7 @@ public class KeyboardLayoutSet {
|
||||||
|
|
||||||
private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo();
|
private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo();
|
||||||
|
|
||||||
public Builder(Context context, EditorInfo editorInfo) {
|
public Builder(final Context context, final EditorInfo editorInfo) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mPackageName = context.getPackageName();
|
mPackageName = context.getPackageName();
|
||||||
mResources = context.getResources();
|
mResources = context.getResources();
|
||||||
|
@ -238,7 +224,8 @@ public class KeyboardLayoutSet {
|
||||||
mPackageName, NO_SETTINGS_KEY, mEditorInfo);
|
mPackageName, NO_SETTINGS_KEY, mEditorInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setScreenGeometry(int deviceFormFactor, int orientation, int widthPixels) {
|
public Builder setScreenGeometry(final int deviceFormFactor, final int orientation,
|
||||||
|
final int widthPixels) {
|
||||||
final Params params = mParams;
|
final Params params = mParams;
|
||||||
params.mDeviceFormFactor = deviceFormFactor;
|
params.mDeviceFormFactor = deviceFormFactor;
|
||||||
params.mOrientation = orientation;
|
params.mOrientation = orientation;
|
||||||
|
@ -246,7 +233,7 @@ public class KeyboardLayoutSet {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setSubtype(InputMethodSubtype subtype) {
|
public Builder setSubtype(final InputMethodSubtype subtype) {
|
||||||
final boolean asciiCapable = subtype.containsExtraValueKey(ASCII_CAPABLE);
|
final boolean asciiCapable = subtype.containsExtraValueKey(ASCII_CAPABLE);
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions(
|
final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions(
|
||||||
|
@ -263,8 +250,8 @@ public class KeyboardLayoutSet {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setOptions(boolean voiceKeyEnabled, boolean voiceKeyOnMain,
|
public Builder setOptions(final boolean voiceKeyEnabled, final boolean voiceKeyOnMain,
|
||||||
boolean languageSwitchKeyEnabled) {
|
final boolean languageSwitchKeyEnabled) {
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions(
|
final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions(
|
||||||
null, NO_MICROPHONE_COMPAT, mEditorInfo);
|
null, NO_MICROPHONE_COMPAT, mEditorInfo);
|
||||||
|
@ -277,7 +264,7 @@ public class KeyboardLayoutSet {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTouchPositionCorrectionEnabled(boolean enabled) {
|
public void setTouchPositionCorrectionEnabled(final boolean enabled) {
|
||||||
mParams.mTouchPositionCorrectionEnabled = enabled;
|
mParams.mTouchPositionCorrectionEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +285,7 @@ public class KeyboardLayoutSet {
|
||||||
return new KeyboardLayoutSet(mContext, mParams);
|
return new KeyboardLayoutSet(mContext, mParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseKeyboardLayoutSet(Resources res, int resId)
|
private void parseKeyboardLayoutSet(final Resources res, final int resId)
|
||||||
throws XmlPullParserException, IOException {
|
throws XmlPullParserException, IOException {
|
||||||
final XmlResourceParser parser = res.getXml(resId);
|
final XmlResourceParser parser = res.getXml(resId);
|
||||||
try {
|
try {
|
||||||
|
@ -318,7 +305,7 @@ public class KeyboardLayoutSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseKeyboardLayoutSetContent(XmlPullParser parser)
|
private void parseKeyboardLayoutSetContent(final XmlPullParser parser)
|
||||||
throws XmlPullParserException, IOException {
|
throws XmlPullParserException, IOException {
|
||||||
int event;
|
int event;
|
||||||
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||||
|
@ -340,7 +327,7 @@ public class KeyboardLayoutSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseKeyboardLayoutSetElement(XmlPullParser parser)
|
private void parseKeyboardLayoutSetElement(final XmlPullParser parser)
|
||||||
throws XmlPullParserException, IOException {
|
throws XmlPullParserException, IOException {
|
||||||
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
|
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
R.styleable.KeyboardLayoutSet_Element);
|
R.styleable.KeyboardLayoutSet_Element);
|
||||||
|
@ -367,7 +354,7 @@ public class KeyboardLayoutSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getKeyboardMode(EditorInfo editorInfo) {
|
private static int getKeyboardMode(final EditorInfo editorInfo) {
|
||||||
if (editorInfo == null)
|
if (editorInfo == null)
|
||||||
return KeyboardId.MODE_TEXT;
|
return KeyboardId.MODE_TEXT;
|
||||||
|
|
||||||
|
|
|
@ -20,15 +20,17 @@ import android.graphics.Paint;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.internal.KeySpecParser.MoreKeySpec;
|
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
|
||||||
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.MoreKeySpec;
|
||||||
import com.android.inputmethod.latin.R;
|
import com.android.inputmethod.latin.R;
|
||||||
import com.android.inputmethod.latin.StringUtils;
|
import com.android.inputmethod.latin.StringUtils;
|
||||||
|
|
||||||
public class MoreKeysKeyboard extends Keyboard {
|
public class MoreKeysKeyboard extends Keyboard {
|
||||||
private final int mDefaultKeyCoordX;
|
private final int mDefaultKeyCoordX;
|
||||||
|
|
||||||
MoreKeysKeyboard(Builder.MoreKeysKeyboardParams params) {
|
MoreKeysKeyboard(final MoreKeysKeyboardParams params) {
|
||||||
super(params);
|
super(params);
|
||||||
mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2;
|
mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2;
|
||||||
}
|
}
|
||||||
|
@ -37,220 +39,222 @@ public class MoreKeysKeyboard extends Keyboard {
|
||||||
return mDefaultKeyCoordX;
|
return mDefaultKeyCoordX;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder extends Keyboard.Builder<Builder.MoreKeysKeyboardParams> {
|
/* package for test */
|
||||||
|
static class MoreKeysKeyboardParams extends KeyboardParams {
|
||||||
|
public boolean mIsFixedOrder;
|
||||||
|
/* package */int mTopRowAdjustment;
|
||||||
|
public int mNumRows;
|
||||||
|
public int mNumColumns;
|
||||||
|
public int mTopKeys;
|
||||||
|
public int mLeftKeys;
|
||||||
|
public int mRightKeys; // includes default key.
|
||||||
|
public int mDividerWidth;
|
||||||
|
public int mColumnWidth;
|
||||||
|
|
||||||
|
public MoreKeysKeyboardParams() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set keyboard parameters of more keys keyboard.
|
||||||
|
*
|
||||||
|
* @param numKeys number of keys in this more keys keyboard.
|
||||||
|
* @param maxColumns number of maximum columns of this more keys keyboard.
|
||||||
|
* @param keyWidth more keys keyboard key width in pixel, including horizontal gap.
|
||||||
|
* @param rowHeight more keys keyboard row height in pixel, including vertical gap.
|
||||||
|
* @param coordXInParent coordinate x of the key preview in parent keyboard.
|
||||||
|
* @param parentKeyboardWidth parent keyboard width in pixel.
|
||||||
|
* @param isFixedColumnOrder if true, more keys should be laid out in fixed order.
|
||||||
|
* @param dividerWidth width of divider, zero for no dividers.
|
||||||
|
*/
|
||||||
|
public void setParameters(final int numKeys, final int maxColumns, final int keyWidth,
|
||||||
|
final int rowHeight, final int coordXInParent, final int parentKeyboardWidth,
|
||||||
|
final boolean isFixedColumnOrder, final int dividerWidth) {
|
||||||
|
mIsFixedOrder = isFixedColumnOrder;
|
||||||
|
if (parentKeyboardWidth / keyWidth < maxColumns) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Keyboard is too small to hold more keys keyboard: "
|
||||||
|
+ parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
|
||||||
|
}
|
||||||
|
mDefaultKeyWidth = keyWidth;
|
||||||
|
mDefaultRowHeight = rowHeight;
|
||||||
|
|
||||||
|
final int numRows = (numKeys + maxColumns - 1) / maxColumns;
|
||||||
|
mNumRows = numRows;
|
||||||
|
final int numColumns = mIsFixedOrder ? Math.min(numKeys, maxColumns)
|
||||||
|
: getOptimizedColumns(numKeys, maxColumns);
|
||||||
|
mNumColumns = numColumns;
|
||||||
|
final int topKeys = numKeys % numColumns;
|
||||||
|
mTopKeys = topKeys == 0 ? numColumns : topKeys;
|
||||||
|
|
||||||
|
final int numLeftKeys = (numColumns - 1) / 2;
|
||||||
|
final int numRightKeys = numColumns - numLeftKeys; // including default key.
|
||||||
|
// Maximum number of keys we can layout both side of the parent key
|
||||||
|
final int maxLeftKeys = coordXInParent / keyWidth;
|
||||||
|
final int maxRightKeys = (parentKeyboardWidth - coordXInParent) / keyWidth;
|
||||||
|
int leftKeys, rightKeys;
|
||||||
|
if (numLeftKeys > maxLeftKeys) {
|
||||||
|
leftKeys = maxLeftKeys;
|
||||||
|
rightKeys = numColumns - leftKeys;
|
||||||
|
} else if (numRightKeys > maxRightKeys + 1) {
|
||||||
|
rightKeys = maxRightKeys + 1; // include default key
|
||||||
|
leftKeys = numColumns - rightKeys;
|
||||||
|
} else {
|
||||||
|
leftKeys = numLeftKeys;
|
||||||
|
rightKeys = numRightKeys;
|
||||||
|
}
|
||||||
|
// If the left keys fill the left side of the parent key, entire more keys keyboard
|
||||||
|
// should be shifted to the right unless the parent key is on the left edge.
|
||||||
|
if (maxLeftKeys == leftKeys && leftKeys > 0) {
|
||||||
|
leftKeys--;
|
||||||
|
rightKeys++;
|
||||||
|
}
|
||||||
|
// If the right keys fill the right side of the parent key, entire more keys
|
||||||
|
// should be shifted to the left unless the parent key is on the right edge.
|
||||||
|
if (maxRightKeys == rightKeys - 1 && rightKeys > 1) {
|
||||||
|
leftKeys++;
|
||||||
|
rightKeys--;
|
||||||
|
}
|
||||||
|
mLeftKeys = leftKeys;
|
||||||
|
mRightKeys = rightKeys;
|
||||||
|
|
||||||
|
// Adjustment of the top row.
|
||||||
|
mTopRowAdjustment = mIsFixedOrder ? getFixedOrderTopRowAdjustment()
|
||||||
|
: getAutoOrderTopRowAdjustment();
|
||||||
|
mDividerWidth = dividerWidth;
|
||||||
|
mColumnWidth = mDefaultKeyWidth + mDividerWidth;
|
||||||
|
mBaseWidth = mOccupiedWidth = mNumColumns * mColumnWidth - mDividerWidth;
|
||||||
|
// Need to subtract the bottom row's gutter only.
|
||||||
|
mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap
|
||||||
|
+ mTopPadding + mBottomPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getFixedOrderTopRowAdjustment() {
|
||||||
|
if (mNumRows == 1 || mTopKeys % 2 == 1 || mTopKeys == mNumColumns
|
||||||
|
|| mLeftKeys == 0 || mRightKeys == 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getAutoOrderTopRowAdjustment() {
|
||||||
|
if (mNumRows == 1 || mTopKeys == 1 || mNumColumns % 2 == mTopKeys % 2
|
||||||
|
|| mLeftKeys == 0 || mRightKeys == 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return key position according to column count (0 is default).
|
||||||
|
/* package */int getColumnPos(final int n) {
|
||||||
|
return mIsFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getFixedOrderColumnPos(final int n) {
|
||||||
|
final int col = n % mNumColumns;
|
||||||
|
final int row = n / mNumColumns;
|
||||||
|
if (!isTopRow(row)) {
|
||||||
|
return col - mLeftKeys;
|
||||||
|
}
|
||||||
|
final int rightSideKeys = mTopKeys / 2;
|
||||||
|
final int leftSideKeys = mTopKeys - (rightSideKeys + 1);
|
||||||
|
final int pos = col - leftSideKeys;
|
||||||
|
final int numLeftKeys = mLeftKeys + mTopRowAdjustment;
|
||||||
|
final int numRightKeys = mRightKeys - 1;
|
||||||
|
if (numRightKeys >= rightSideKeys && numLeftKeys >= leftSideKeys) {
|
||||||
|
return pos;
|
||||||
|
} else if (numRightKeys < rightSideKeys) {
|
||||||
|
return pos - (rightSideKeys - numRightKeys);
|
||||||
|
} else { // numLeftKeys < leftSideKeys
|
||||||
|
return pos + (leftSideKeys - numLeftKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getAutomaticColumnPos(final int n) {
|
||||||
|
final int col = n % mNumColumns;
|
||||||
|
final int row = n / mNumColumns;
|
||||||
|
int leftKeys = mLeftKeys;
|
||||||
|
if (isTopRow(row)) {
|
||||||
|
leftKeys += mTopRowAdjustment;
|
||||||
|
}
|
||||||
|
if (col == 0) {
|
||||||
|
// default position.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
int right = 1; // include default position key.
|
||||||
|
int left = 0;
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
// Assign right key if available.
|
||||||
|
if (right < mRightKeys) {
|
||||||
|
pos = right;
|
||||||
|
right++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i >= col)
|
||||||
|
break;
|
||||||
|
// Assign left key if available.
|
||||||
|
if (left < leftKeys) {
|
||||||
|
left++;
|
||||||
|
pos = -left;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i >= col)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getTopRowEmptySlots(final int numKeys, final int numColumns) {
|
||||||
|
final int remainings = numKeys % numColumns;
|
||||||
|
return remainings == 0 ? 0 : numColumns - remainings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getOptimizedColumns(final int numKeys, final int maxColumns) {
|
||||||
|
int numColumns = Math.min(numKeys, maxColumns);
|
||||||
|
while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
|
||||||
|
numColumns--;
|
||||||
|
}
|
||||||
|
return numColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDefaultKeyCoordX() {
|
||||||
|
return mLeftKeys * mColumnWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX(final int n, final int row) {
|
||||||
|
final int x = getColumnPos(n) * mColumnWidth + getDefaultKeyCoordX();
|
||||||
|
if (isTopRow(row)) {
|
||||||
|
return x + mTopRowAdjustment * (mColumnWidth / 2);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY(final int row) {
|
||||||
|
return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markAsEdgeKey(final Key key, final int row) {
|
||||||
|
if (row == 0)
|
||||||
|
key.markAsTopEdge(this);
|
||||||
|
if (isTopRow(row))
|
||||||
|
key.markAsBottomEdge(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTopRow(final int rowCount) {
|
||||||
|
return mNumRows > 1 && rowCount == mNumRows - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder extends KeyboardBuilder<MoreKeysKeyboardParams> {
|
||||||
private final Key mParentKey;
|
private final Key mParentKey;
|
||||||
private final Drawable mDivider;
|
private final Drawable mDivider;
|
||||||
|
|
||||||
private static final float LABEL_PADDING_RATIO = 0.2f;
|
private static final float LABEL_PADDING_RATIO = 0.2f;
|
||||||
private static final float DIVIDER_RATIO = 0.2f;
|
private static final float DIVIDER_RATIO = 0.2f;
|
||||||
|
|
||||||
public static class MoreKeysKeyboardParams extends Keyboard.Params {
|
|
||||||
public boolean mIsFixedOrder;
|
|
||||||
/* package */int mTopRowAdjustment;
|
|
||||||
public int mNumRows;
|
|
||||||
public int mNumColumns;
|
|
||||||
public int mTopKeys;
|
|
||||||
public int mLeftKeys;
|
|
||||||
public int mRightKeys; // includes default key.
|
|
||||||
public int mDividerWidth;
|
|
||||||
public int mColumnWidth;
|
|
||||||
|
|
||||||
public MoreKeysKeyboardParams() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set keyboard parameters of more keys keyboard.
|
|
||||||
*
|
|
||||||
* @param numKeys number of keys in this more keys keyboard.
|
|
||||||
* @param maxColumns number of maximum columns of this more keys keyboard.
|
|
||||||
* @param keyWidth more keys keyboard key width in pixel, including horizontal gap.
|
|
||||||
* @param rowHeight more keys keyboard row height in pixel, including vertical gap.
|
|
||||||
* @param coordXInParent coordinate x of the key preview in parent keyboard.
|
|
||||||
* @param parentKeyboardWidth parent keyboard width in pixel.
|
|
||||||
* @param isFixedColumnOrder if true, more keys should be laid out in fixed order.
|
|
||||||
* @param dividerWidth width of divider, zero for no dividers.
|
|
||||||
*/
|
|
||||||
public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight,
|
|
||||||
int coordXInParent, int parentKeyboardWidth, boolean isFixedColumnOrder,
|
|
||||||
int dividerWidth) {
|
|
||||||
mIsFixedOrder = isFixedColumnOrder;
|
|
||||||
if (parentKeyboardWidth / keyWidth < maxColumns) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Keyboard is too small to hold more keys keyboard: "
|
|
||||||
+ parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
|
|
||||||
}
|
|
||||||
mDefaultKeyWidth = keyWidth;
|
|
||||||
mDefaultRowHeight = rowHeight;
|
|
||||||
|
|
||||||
final int numRows = (numKeys + maxColumns - 1) / maxColumns;
|
|
||||||
mNumRows = numRows;
|
|
||||||
final int numColumns = mIsFixedOrder ? Math.min(numKeys, maxColumns)
|
|
||||||
: getOptimizedColumns(numKeys, maxColumns);
|
|
||||||
mNumColumns = numColumns;
|
|
||||||
final int topKeys = numKeys % numColumns;
|
|
||||||
mTopKeys = topKeys == 0 ? numColumns : topKeys;
|
|
||||||
|
|
||||||
final int numLeftKeys = (numColumns - 1) / 2;
|
|
||||||
final int numRightKeys = numColumns - numLeftKeys; // including default key.
|
|
||||||
// Maximum number of keys we can layout both side of the parent key
|
|
||||||
final int maxLeftKeys = coordXInParent / keyWidth;
|
|
||||||
final int maxRightKeys = (parentKeyboardWidth - coordXInParent) / keyWidth;
|
|
||||||
int leftKeys, rightKeys;
|
|
||||||
if (numLeftKeys > maxLeftKeys) {
|
|
||||||
leftKeys = maxLeftKeys;
|
|
||||||
rightKeys = numColumns - leftKeys;
|
|
||||||
} else if (numRightKeys > maxRightKeys + 1) {
|
|
||||||
rightKeys = maxRightKeys + 1; // include default key
|
|
||||||
leftKeys = numColumns - rightKeys;
|
|
||||||
} else {
|
|
||||||
leftKeys = numLeftKeys;
|
|
||||||
rightKeys = numRightKeys;
|
|
||||||
}
|
|
||||||
// If the left keys fill the left side of the parent key, entire more keys keyboard
|
|
||||||
// should be shifted to the right unless the parent key is on the left edge.
|
|
||||||
if (maxLeftKeys == leftKeys && leftKeys > 0) {
|
|
||||||
leftKeys--;
|
|
||||||
rightKeys++;
|
|
||||||
}
|
|
||||||
// If the right keys fill the right side of the parent key, entire more keys
|
|
||||||
// should be shifted to the left unless the parent key is on the right edge.
|
|
||||||
if (maxRightKeys == rightKeys - 1 && rightKeys > 1) {
|
|
||||||
leftKeys++;
|
|
||||||
rightKeys--;
|
|
||||||
}
|
|
||||||
mLeftKeys = leftKeys;
|
|
||||||
mRightKeys = rightKeys;
|
|
||||||
|
|
||||||
// Adjustment of the top row.
|
|
||||||
mTopRowAdjustment = mIsFixedOrder ? getFixedOrderTopRowAdjustment()
|
|
||||||
: getAutoOrderTopRowAdjustment();
|
|
||||||
mDividerWidth = dividerWidth;
|
|
||||||
mColumnWidth = mDefaultKeyWidth + mDividerWidth;
|
|
||||||
mBaseWidth = mOccupiedWidth = mNumColumns * mColumnWidth - mDividerWidth;
|
|
||||||
// Need to subtract the bottom row's gutter only.
|
|
||||||
mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap
|
|
||||||
+ mTopPadding + mBottomPadding;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getFixedOrderTopRowAdjustment() {
|
|
||||||
if (mNumRows == 1 || mTopKeys % 2 == 1 || mTopKeys == mNumColumns
|
|
||||||
|| mLeftKeys == 0 || mRightKeys == 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getAutoOrderTopRowAdjustment() {
|
|
||||||
if (mNumRows == 1 || mTopKeys == 1 || mNumColumns % 2 == mTopKeys % 2
|
|
||||||
|| mLeftKeys == 0 || mRightKeys == 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return key position according to column count (0 is default).
|
|
||||||
/* package */int getColumnPos(int n) {
|
|
||||||
return mIsFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getFixedOrderColumnPos(int n) {
|
|
||||||
final int col = n % mNumColumns;
|
|
||||||
final int row = n / mNumColumns;
|
|
||||||
if (!isTopRow(row)) {
|
|
||||||
return col - mLeftKeys;
|
|
||||||
}
|
|
||||||
final int rightSideKeys = mTopKeys / 2;
|
|
||||||
final int leftSideKeys = mTopKeys - (rightSideKeys + 1);
|
|
||||||
final int pos = col - leftSideKeys;
|
|
||||||
final int numLeftKeys = mLeftKeys + mTopRowAdjustment;
|
|
||||||
final int numRightKeys = mRightKeys - 1;
|
|
||||||
if (numRightKeys >= rightSideKeys && numLeftKeys >= leftSideKeys) {
|
|
||||||
return pos;
|
|
||||||
} else if (numRightKeys < rightSideKeys) {
|
|
||||||
return pos - (rightSideKeys - numRightKeys);
|
|
||||||
} else { // numLeftKeys < leftSideKeys
|
|
||||||
return pos + (leftSideKeys - numLeftKeys);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getAutomaticColumnPos(int n) {
|
|
||||||
final int col = n % mNumColumns;
|
|
||||||
final int row = n / mNumColumns;
|
|
||||||
int leftKeys = mLeftKeys;
|
|
||||||
if (isTopRow(row)) {
|
|
||||||
leftKeys += mTopRowAdjustment;
|
|
||||||
}
|
|
||||||
if (col == 0) {
|
|
||||||
// default position.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
int right = 1; // include default position key.
|
|
||||||
int left = 0;
|
|
||||||
int i = 0;
|
|
||||||
while (true) {
|
|
||||||
// Assign right key if available.
|
|
||||||
if (right < mRightKeys) {
|
|
||||||
pos = right;
|
|
||||||
right++;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i >= col)
|
|
||||||
break;
|
|
||||||
// Assign left key if available.
|
|
||||||
if (left < leftKeys) {
|
|
||||||
left++;
|
|
||||||
pos = -left;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i >= col)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getTopRowEmptySlots(int numKeys, int numColumns) {
|
|
||||||
final int remainings = numKeys % numColumns;
|
|
||||||
return remainings == 0 ? 0 : numColumns - remainings;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getOptimizedColumns(int numKeys, int maxColumns) {
|
|
||||||
int numColumns = Math.min(numKeys, maxColumns);
|
|
||||||
while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
|
|
||||||
numColumns--;
|
|
||||||
}
|
|
||||||
return numColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDefaultKeyCoordX() {
|
|
||||||
return mLeftKeys * mColumnWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getX(int n, int row) {
|
|
||||||
final int x = getColumnPos(n) * mColumnWidth + getDefaultKeyCoordX();
|
|
||||||
if (isTopRow(row)) {
|
|
||||||
return x + mTopRowAdjustment * (mColumnWidth / 2);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getY(int row) {
|
|
||||||
return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void markAsEdgeKey(Key key, int row) {
|
|
||||||
if (row == 0)
|
|
||||||
key.markAsTopEdge(this);
|
|
||||||
if (isTopRow(row))
|
|
||||||
key.markAsBottomEdge(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isTopRow(int rowCount) {
|
|
||||||
return mNumRows > 1 && rowCount == mNumRows - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The builder of MoreKeysKeyboard.
|
* The builder of MoreKeysKeyboard.
|
||||||
|
@ -258,7 +262,8 @@ public class MoreKeysKeyboard extends Keyboard {
|
||||||
* @param parentKey the {@link Key} that invokes more keys keyboard.
|
* @param parentKey the {@link Key} that invokes more keys keyboard.
|
||||||
* @param parentKeyboardView the {@link KeyboardView} that contains the parentKey.
|
* @param parentKeyboardView the {@link KeyboardView} that contains the parentKey.
|
||||||
*/
|
*/
|
||||||
public Builder(View containerView, Key parentKey, KeyboardView parentKeyboardView) {
|
public Builder(final View containerView, final Key parentKey,
|
||||||
|
final KeyboardView parentKeyboardView) {
|
||||||
super(containerView.getContext(), new MoreKeysKeyboardParams());
|
super(containerView.getContext(), new MoreKeysKeyboardParams());
|
||||||
final Keyboard parentKeyboard = parentKeyboardView.getKeyboard();
|
final Keyboard parentKeyboard = parentKeyboardView.getKeyboard();
|
||||||
load(parentKeyboard.mMoreKeysTemplate, parentKeyboard.mId);
|
load(parentKeyboard.mMoreKeysTemplate, parentKeyboard.mId);
|
||||||
|
@ -300,7 +305,8 @@ public class MoreKeysKeyboard extends Keyboard {
|
||||||
dividerWidth);
|
dividerWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getMaxKeyWidth(KeyboardView view, Key parentKey, int minKeyWidth) {
|
private static int getMaxKeyWidth(final KeyboardView view, final Key parentKey,
|
||||||
|
final int minKeyWidth) {
|
||||||
final int padding = (int)(view.getResources()
|
final int padding = (int)(view.getResources()
|
||||||
.getDimension(R.dimen.more_keys_keyboard_key_horizontal_padding)
|
.getDimension(R.dimen.more_keys_keyboard_key_horizontal_padding)
|
||||||
+ (parentKey.hasLabelsInMoreKeys() ? minKeyWidth * LABEL_PADDING_RATIO : 0));
|
+ (parentKey.hasLabelsInMoreKeys() ? minKeyWidth * LABEL_PADDING_RATIO : 0));
|
||||||
|
@ -322,24 +328,6 @@ public class MoreKeysKeyboard extends Keyboard {
|
||||||
return maxWidth;
|
return maxWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MoreKeyDivider extends Key.Spacer {
|
|
||||||
private final Drawable mIcon;
|
|
||||||
|
|
||||||
public MoreKeyDivider(MoreKeysKeyboardParams params, Drawable icon, int x, int y) {
|
|
||||||
super(params, x, y, params.mDividerWidth, params.mDefaultRowHeight);
|
|
||||||
mIcon = icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Drawable getIcon(KeyboardIconsSet iconSet, int alpha) {
|
|
||||||
// KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the
|
|
||||||
// constructor.
|
|
||||||
// TODO: Drawable itself should have an alpha value.
|
|
||||||
mIcon.setAlpha(128);
|
|
||||||
return mIcon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MoreKeysKeyboard build() {
|
public MoreKeysKeyboard build() {
|
||||||
final MoreKeysKeyboardParams params = mParams;
|
final MoreKeysKeyboardParams params = mParams;
|
||||||
|
@ -368,4 +356,23 @@ public class MoreKeysKeyboard extends Keyboard {
|
||||||
return new MoreKeysKeyboard(params);
|
return new MoreKeysKeyboard(params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MoreKeyDivider extends Key.Spacer {
|
||||||
|
private final Drawable mIcon;
|
||||||
|
|
||||||
|
public MoreKeyDivider(final MoreKeysKeyboardParams params, final Drawable icon,
|
||||||
|
final int x, final int y) {
|
||||||
|
super(params, x, y, params.mDividerWidth, params.mDefaultRowHeight);
|
||||||
|
mIcon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) {
|
||||||
|
// KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the
|
||||||
|
// constructor.
|
||||||
|
// TODO: Drawable itself should have an alpha value.
|
||||||
|
mIcon.setAlpha(128);
|
||||||
|
return mIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ package com.android.inputmethod.keyboard;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection;
|
import com.android.inputmethod.keyboard.internal.TouchPositionCorrection;
|
||||||
import com.android.inputmethod.latin.Constants;
|
import com.android.inputmethod.latin.Constants;
|
||||||
import com.android.inputmethod.latin.JniUtils;
|
import com.android.inputmethod.latin.JniUtils;
|
||||||
|
|
||||||
|
@ -48,9 +48,10 @@ public class ProximityInfo {
|
||||||
private final Key[][] mGridNeighbors;
|
private final Key[][] mGridNeighbors;
|
||||||
private final String mLocaleStr;
|
private final String mLocaleStr;
|
||||||
|
|
||||||
ProximityInfo(String localeStr, int gridWidth, int gridHeight, int minWidth, int height,
|
ProximityInfo(final String localeStr, final int gridWidth, final int gridHeight,
|
||||||
int mostCommonKeyWidth, int mostCommonKeyHeight, final Key[] keys,
|
final int minWidth, final int height, final int mostCommonKeyWidth,
|
||||||
TouchPositionCorrection touchPositionCorrection) {
|
final int mostCommonKeyHeight, final Key[] keys,
|
||||||
|
final TouchPositionCorrection touchPositionCorrection) {
|
||||||
if (TextUtils.isEmpty(localeStr)) {
|
if (TextUtils.isEmpty(localeStr)) {
|
||||||
mLocaleStr = "";
|
mLocaleStr = "";
|
||||||
} else {
|
} else {
|
||||||
|
@ -81,7 +82,7 @@ public class ProximityInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity,
|
public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity,
|
||||||
int rowSize, int gridWidth, int gridHeight) {
|
final int rowSize, final int gridWidth, final int gridHeight) {
|
||||||
final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
|
final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
|
||||||
spellCheckerProximityInfo.mNativeProximityInfo =
|
spellCheckerProximityInfo.mNativeProximityInfo =
|
||||||
spellCheckerProximityInfo.setProximityInfoNative("",
|
spellCheckerProximityInfo.setProximityInfoNative("",
|
||||||
|
|
|
@ -56,59 +56,20 @@ public class KeySpecParser {
|
||||||
private static final char ESCAPE_CHAR = '\\';
|
private static final char ESCAPE_CHAR = '\\';
|
||||||
private static final char LABEL_END = '|';
|
private static final char LABEL_END = '|';
|
||||||
private static final String PREFIX_TEXT = "!text/";
|
private static final String PREFIX_TEXT = "!text/";
|
||||||
private static final String PREFIX_ICON = "!icon/";
|
static final String PREFIX_ICON = "!icon/";
|
||||||
private static final String PREFIX_CODE = "!code/";
|
private static final String PREFIX_CODE = "!code/";
|
||||||
private static final String PREFIX_HEX = "0x";
|
private static final String PREFIX_HEX = "0x";
|
||||||
private static final String ADDITIONAL_MORE_KEY_MARKER = "%";
|
private static final String ADDITIONAL_MORE_KEY_MARKER = "%";
|
||||||
|
|
||||||
public static class MoreKeySpec {
|
|
||||||
public final int mCode;
|
|
||||||
public final String mLabel;
|
|
||||||
public final String mOutputText;
|
|
||||||
public final int mIconId;
|
|
||||||
|
|
||||||
public MoreKeySpec(final String moreKeySpec, boolean needsToUpperCase, Locale locale,
|
|
||||||
final KeyboardCodesSet codesSet) {
|
|
||||||
mLabel = toUpperCaseOfStringForLocale(getLabel(moreKeySpec),
|
|
||||||
needsToUpperCase, locale);
|
|
||||||
final int code = toUpperCaseOfCodeForLocale(getCode(moreKeySpec, codesSet),
|
|
||||||
needsToUpperCase, locale);
|
|
||||||
if (code == Keyboard.CODE_UNSPECIFIED) {
|
|
||||||
// Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters
|
|
||||||
// upper case representation ("SS").
|
|
||||||
mCode = Keyboard.CODE_OUTPUT_TEXT;
|
|
||||||
mOutputText = mLabel;
|
|
||||||
} else {
|
|
||||||
mCode = code;
|
|
||||||
mOutputText = toUpperCaseOfStringForLocale(getOutputText(moreKeySpec),
|
|
||||||
needsToUpperCase, locale);
|
|
||||||
}
|
|
||||||
mIconId = getIconId(moreKeySpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel
|
|
||||||
: PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId));
|
|
||||||
final String output = (mCode == Keyboard.CODE_OUTPUT_TEXT ? mOutputText
|
|
||||||
: Keyboard.printableCode(mCode));
|
|
||||||
if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) {
|
|
||||||
return output;
|
|
||||||
} else {
|
|
||||||
return label + "|" + output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private KeySpecParser() {
|
private KeySpecParser() {
|
||||||
// Intentional empty constructor for utility class.
|
// Intentional empty constructor for utility class.
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasIcon(String moreKeySpec) {
|
private static boolean hasIcon(final String moreKeySpec) {
|
||||||
return moreKeySpec.startsWith(PREFIX_ICON);
|
return moreKeySpec.startsWith(PREFIX_ICON);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasCode(String moreKeySpec) {
|
private static boolean hasCode(final String moreKeySpec) {
|
||||||
final int end = indexOfLabelEnd(moreKeySpec, 0);
|
final int end = indexOfLabelEnd(moreKeySpec, 0);
|
||||||
if (end > 0 && end + 1 < moreKeySpec.length() && moreKeySpec.startsWith(
|
if (end > 0 && end + 1 < moreKeySpec.length() && moreKeySpec.startsWith(
|
||||||
PREFIX_CODE, end + 1)) {
|
PREFIX_CODE, end + 1)) {
|
||||||
|
@ -117,7 +78,7 @@ public class KeySpecParser {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String parseEscape(String text) {
|
private static String parseEscape(final String text) {
|
||||||
if (text.indexOf(ESCAPE_CHAR) < 0) {
|
if (text.indexOf(ESCAPE_CHAR) < 0) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
@ -136,7 +97,7 @@ public class KeySpecParser {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int indexOfLabelEnd(String moreKeySpec, int start) {
|
private static int indexOfLabelEnd(final String moreKeySpec, final int start) {
|
||||||
if (moreKeySpec.indexOf(ESCAPE_CHAR, start) < 0) {
|
if (moreKeySpec.indexOf(ESCAPE_CHAR, start) < 0) {
|
||||||
final int end = moreKeySpec.indexOf(LABEL_END, start);
|
final int end = moreKeySpec.indexOf(LABEL_END, start);
|
||||||
if (end == 0) {
|
if (end == 0) {
|
||||||
|
@ -157,7 +118,7 @@ public class KeySpecParser {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getLabel(String moreKeySpec) {
|
public static String getLabel(final String moreKeySpec) {
|
||||||
if (hasIcon(moreKeySpec)) {
|
if (hasIcon(moreKeySpec)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -170,7 +131,7 @@ public class KeySpecParser {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getOutputTextInternal(String moreKeySpec) {
|
private static String getOutputTextInternal(final String moreKeySpec) {
|
||||||
final int end = indexOfLabelEnd(moreKeySpec, 0);
|
final int end = indexOfLabelEnd(moreKeySpec, 0);
|
||||||
if (end <= 0) {
|
if (end <= 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -181,7 +142,7 @@ public class KeySpecParser {
|
||||||
return parseEscape(moreKeySpec.substring(end + /* LABEL_END */1));
|
return parseEscape(moreKeySpec.substring(end + /* LABEL_END */1));
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getOutputText(String moreKeySpec) {
|
static String getOutputText(final String moreKeySpec) {
|
||||||
if (hasCode(moreKeySpec)) {
|
if (hasCode(moreKeySpec)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -205,7 +166,7 @@ public class KeySpecParser {
|
||||||
return (StringUtils.codePointCount(label) == 1) ? null : label;
|
return (StringUtils.codePointCount(label) == 1) ? null : label;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getCode(String moreKeySpec, KeyboardCodesSet codesSet) {
|
static int getCode(final String moreKeySpec, final KeyboardCodesSet codesSet) {
|
||||||
if (hasCode(moreKeySpec)) {
|
if (hasCode(moreKeySpec)) {
|
||||||
final int end = indexOfLabelEnd(moreKeySpec, 0);
|
final int end = indexOfLabelEnd(moreKeySpec, 0);
|
||||||
if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) {
|
if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) {
|
||||||
|
@ -230,7 +191,8 @@ public class KeySpecParser {
|
||||||
return Keyboard.CODE_OUTPUT_TEXT;
|
return Keyboard.CODE_OUTPUT_TEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int parseCode(String text, KeyboardCodesSet codesSet, int defCode) {
|
public static int parseCode(final String text, final KeyboardCodesSet codesSet,
|
||||||
|
final int defCode) {
|
||||||
if (text == null) return defCode;
|
if (text == null) return defCode;
|
||||||
if (text.startsWith(PREFIX_CODE)) {
|
if (text.startsWith(PREFIX_CODE)) {
|
||||||
return codesSet.getCode(text.substring(PREFIX_CODE.length()));
|
return codesSet.getCode(text.substring(PREFIX_CODE.length()));
|
||||||
|
@ -241,7 +203,7 @@ public class KeySpecParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getIconId(String moreKeySpec) {
|
public static int getIconId(final String moreKeySpec) {
|
||||||
if (moreKeySpec != null && hasIcon(moreKeySpec)) {
|
if (moreKeySpec != null && hasIcon(moreKeySpec)) {
|
||||||
final int end = moreKeySpec.indexOf(LABEL_END, PREFIX_ICON.length());
|
final int end = moreKeySpec.indexOf(LABEL_END, PREFIX_ICON.length());
|
||||||
final String name = (end < 0) ? moreKeySpec.substring(PREFIX_ICON.length())
|
final String name = (end < 0) ? moreKeySpec.substring(PREFIX_ICON.length())
|
||||||
|
@ -251,7 +213,7 @@ public class KeySpecParser {
|
||||||
return KeyboardIconsSet.ICON_UNDEFINED;
|
return KeyboardIconsSet.ICON_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> ArrayList<T> arrayAsList(T[] array, int start, int end) {
|
private static <T> ArrayList<T> arrayAsList(final T[] array, final int start, final int end) {
|
||||||
if (array == null) {
|
if (array == null) {
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
}
|
}
|
||||||
|
@ -268,7 +230,7 @@ public class KeySpecParser {
|
||||||
|
|
||||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||||
|
|
||||||
private static String[] filterOutEmptyString(String[] array) {
|
private static String[] filterOutEmptyString(final String[] array) {
|
||||||
if (array == null) {
|
if (array == null) {
|
||||||
return EMPTY_STRING_ARRAY;
|
return EMPTY_STRING_ARRAY;
|
||||||
}
|
}
|
||||||
|
@ -289,8 +251,8 @@ public class KeySpecParser {
|
||||||
return out.toArray(new String[out.size()]);
|
return out.toArray(new String[out.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String[] insertAdditionalMoreKeys(String[] moreKeySpecs,
|
public static String[] insertAdditionalMoreKeys(final String[] moreKeySpecs,
|
||||||
String[] additionalMoreKeySpecs) {
|
final String[] additionalMoreKeySpecs) {
|
||||||
final String[] moreKeys = filterOutEmptyString(moreKeySpecs);
|
final String[] moreKeys = filterOutEmptyString(moreKeySpecs);
|
||||||
final String[] additionalMoreKeys = filterOutEmptyString(additionalMoreKeySpecs);
|
final String[] additionalMoreKeys = filterOutEmptyString(additionalMoreKeySpecs);
|
||||||
final int moreKeysCount = moreKeys.length;
|
final int moreKeysCount = moreKeys.length;
|
||||||
|
@ -357,12 +319,13 @@ public class KeySpecParser {
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public static class KeySpecParserError extends RuntimeException {
|
public static class KeySpecParserError extends RuntimeException {
|
||||||
public KeySpecParserError(String message) {
|
public KeySpecParserError(final String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String resolveTextReference(String rawText, KeyboardTextsSet textsSet) {
|
public static String resolveTextReference(final String rawText,
|
||||||
|
final KeyboardTextsSet textsSet) {
|
||||||
int level = 0;
|
int level = 0;
|
||||||
String text = rawText;
|
String text = rawText;
|
||||||
StringBuilder sb;
|
StringBuilder sb;
|
||||||
|
@ -408,7 +371,7 @@ public class KeySpecParser {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int searchTextNameEnd(String text, int start) {
|
private static int searchTextNameEnd(final String text, final int start) {
|
||||||
final int size = text.length();
|
final int size = text.length();
|
||||||
for (int pos = start; pos < size; pos++) {
|
for (int pos = start; pos < size; pos++) {
|
||||||
final char c = text.charAt(pos);
|
final char c = text.charAt(pos);
|
||||||
|
@ -421,7 +384,7 @@ public class KeySpecParser {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String[] parseCsvString(String rawText, KeyboardTextsSet textsSet) {
|
public static String[] parseCsvString(final String rawText, final KeyboardTextsSet textsSet) {
|
||||||
final String text = resolveTextReference(rawText, textsSet);
|
final String text = resolveTextReference(rawText, textsSet);
|
||||||
final int size = text.length();
|
final int size = text.length();
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
|
@ -460,7 +423,8 @@ public class KeySpecParser {
|
||||||
return list.toArray(new String[list.size()]);
|
return list.toArray(new String[list.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getIntValue(String[] moreKeys, String key, int defaultValue) {
|
public static int getIntValue(final String[] moreKeys, final String key,
|
||||||
|
final int defaultValue) {
|
||||||
if (moreKeys == null) {
|
if (moreKeys == null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
@ -486,7 +450,7 @@ public class KeySpecParser {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean getBooleanValue(String[] moreKeys, String key) {
|
public static boolean getBooleanValue(final String[] moreKeys, final String key) {
|
||||||
if (moreKeys == null) {
|
if (moreKeys == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -502,8 +466,8 @@ public class KeySpecParser {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int toUpperCaseOfCodeForLocale(int code, boolean needsToUpperCase,
|
public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase,
|
||||||
Locale locale) {
|
final Locale locale) {
|
||||||
if (!Keyboard.isLetterCode(code) || !needsToUpperCase) return code;
|
if (!Keyboard.isLetterCode(code) || !needsToUpperCase) return code;
|
||||||
final String text = new String(new int[] { code } , 0, 1);
|
final String text = new String(new int[] { code } , 0, 1);
|
||||||
final String casedText = KeySpecParser.toUpperCaseOfStringForLocale(
|
final String casedText = KeySpecParser.toUpperCaseOfStringForLocale(
|
||||||
|
@ -512,8 +476,8 @@ public class KeySpecParser {
|
||||||
? casedText.codePointAt(0) : CODE_UNSPECIFIED;
|
? casedText.codePointAt(0) : CODE_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toUpperCaseOfStringForLocale(String text, boolean needsToUpperCase,
|
public static String toUpperCaseOfStringForLocale(final String text,
|
||||||
Locale locale) {
|
final boolean needsToUpperCase, final Locale locale) {
|
||||||
if (text == null || !needsToUpperCase) return text;
|
if (text == null || !needsToUpperCase) return text;
|
||||||
return text.toUpperCase(locale);
|
return text.toUpperCase(locale);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.inputmethod.keyboard.internal;
|
||||||
|
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
|
||||||
|
public abstract class KeyStyle {
|
||||||
|
private final KeyboardTextsSet mTextsSet;
|
||||||
|
|
||||||
|
public abstract String[] getStringArray(TypedArray a, int index);
|
||||||
|
public abstract String getString(TypedArray a, int index);
|
||||||
|
public abstract int getInt(TypedArray a, int index, int defaultValue);
|
||||||
|
public abstract int getFlag(TypedArray a, int index);
|
||||||
|
|
||||||
|
protected KeyStyle(final KeyboardTextsSet textsSet) {
|
||||||
|
mTextsSet = textsSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String parseString(final TypedArray a, final int index) {
|
||||||
|
if (a.hasValue(index)) {
|
||||||
|
return KeySpecParser.resolveTextReference(a.getString(index), mTextsSet);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String[] parseStringArray(final TypedArray a, final int index) {
|
||||||
|
if (a.hasValue(index)) {
|
||||||
|
return KeySpecParser.parseCsvString(a.getString(index), mTextsSet);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,6 @@ import android.content.res.TypedArray;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.Keyboard;
|
|
||||||
import com.android.inputmethod.latin.CollectionUtils;
|
import com.android.inputmethod.latin.CollectionUtils;
|
||||||
import com.android.inputmethod.latin.R;
|
import com.android.inputmethod.latin.R;
|
||||||
import com.android.inputmethod.latin.XmlParseUtils;
|
import com.android.inputmethod.latin.XmlParseUtils;
|
||||||
|
@ -30,75 +29,62 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class KeyStyles {
|
public class KeyStylesSet {
|
||||||
private static final String TAG = KeyStyles.class.getSimpleName();
|
private static final String TAG = KeyStylesSet.class.getSimpleName();
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
final HashMap<String, KeyStyle> mStyles = CollectionUtils.newHashMap();
|
private final HashMap<String, KeyStyle> mStyles = CollectionUtils.newHashMap();
|
||||||
|
|
||||||
final KeyboardTextsSet mTextsSet;
|
private final KeyboardTextsSet mTextsSet;
|
||||||
private final KeyStyle mEmptyKeyStyle;
|
private final KeyStyle mEmptyKeyStyle;
|
||||||
private static final String EMPTY_STYLE_NAME = "<empty>";
|
private static final String EMPTY_STYLE_NAME = "<empty>";
|
||||||
|
|
||||||
public KeyStyles(KeyboardTextsSet textsSet) {
|
public KeyStylesSet(final KeyboardTextsSet textsSet) {
|
||||||
mTextsSet = textsSet;
|
mTextsSet = textsSet;
|
||||||
mEmptyKeyStyle = new EmptyKeyStyle();
|
mEmptyKeyStyle = new EmptyKeyStyle(textsSet);
|
||||||
mStyles.put(EMPTY_STYLE_NAME, mEmptyKeyStyle);
|
mStyles.put(EMPTY_STYLE_NAME, mEmptyKeyStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class KeyStyle {
|
private static class EmptyKeyStyle extends KeyStyle {
|
||||||
public abstract String[] getStringArray(TypedArray a, int index);
|
EmptyKeyStyle(final KeyboardTextsSet textsSet) {
|
||||||
public abstract String getString(TypedArray a, int index);
|
super(textsSet);
|
||||||
public abstract int getInt(TypedArray a, int index, int defaultValue);
|
|
||||||
public abstract int getFlag(TypedArray a, int index);
|
|
||||||
|
|
||||||
protected String parseString(TypedArray a, int index) {
|
|
||||||
if (a.hasValue(index)) {
|
|
||||||
return KeySpecParser.resolveTextReference(a.getString(index), mTextsSet);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String[] parseStringArray(TypedArray a, int index) {
|
|
||||||
if (a.hasValue(index)) {
|
|
||||||
return KeySpecParser.parseCsvString(a.getString(index), mTextsSet);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EmptyKeyStyle extends KeyStyle {
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getStringArray(TypedArray a, int index) {
|
public String[] getStringArray(final TypedArray a, final int index) {
|
||||||
return parseStringArray(a, index);
|
return parseStringArray(a, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getString(TypedArray a, int index) {
|
public String getString(final TypedArray a, final int index) {
|
||||||
return parseString(a, index);
|
return parseString(a, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInt(TypedArray a, int index, int defaultValue) {
|
public int getInt(final TypedArray a, final int index, final int defaultValue) {
|
||||||
return a.getInt(index, defaultValue);
|
return a.getInt(index, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFlag(TypedArray a, int index) {
|
public int getFlag(final TypedArray a, final int index) {
|
||||||
return a.getInt(index, 0);
|
return a.getInt(index, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DeclaredKeyStyle extends KeyStyle {
|
private static class DeclaredKeyStyle extends KeyStyle {
|
||||||
|
private final HashMap<String, KeyStyle> mStyles;
|
||||||
private final String mParentStyleName;
|
private final String mParentStyleName;
|
||||||
private final SparseArray<Object> mStyleAttributes = CollectionUtils.newSparseArray();
|
private final SparseArray<Object> mStyleAttributes = CollectionUtils.newSparseArray();
|
||||||
|
|
||||||
public DeclaredKeyStyle(String parentStyleName) {
|
public DeclaredKeyStyle(final String parentStyleName, final KeyboardTextsSet textsSet,
|
||||||
|
final HashMap<String, KeyStyle> styles) {
|
||||||
|
super(textsSet);
|
||||||
mParentStyleName = parentStyleName;
|
mParentStyleName = parentStyleName;
|
||||||
|
mStyles = styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getStringArray(TypedArray a, int index) {
|
public String[] getStringArray(final TypedArray a, final int index) {
|
||||||
if (a.hasValue(index)) {
|
if (a.hasValue(index)) {
|
||||||
return parseStringArray(a, index);
|
return parseStringArray(a, index);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +97,7 @@ public class KeyStyles {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getString(TypedArray a, int index) {
|
public String getString(final TypedArray a, final int index) {
|
||||||
if (a.hasValue(index)) {
|
if (a.hasValue(index)) {
|
||||||
return parseString(a, index);
|
return parseString(a, index);
|
||||||
}
|
}
|
||||||
|
@ -124,7 +110,7 @@ public class KeyStyles {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInt(TypedArray a, int index, int defaultValue) {
|
public int getInt(final TypedArray a, final int index, final int defaultValue) {
|
||||||
if (a.hasValue(index)) {
|
if (a.hasValue(index)) {
|
||||||
return a.getInt(index, defaultValue);
|
return a.getInt(index, defaultValue);
|
||||||
}
|
}
|
||||||
|
@ -137,7 +123,7 @@ public class KeyStyles {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFlag(TypedArray a, int index) {
|
public int getFlag(final TypedArray a, final int index) {
|
||||||
int flags = a.getInt(index, 0);
|
int flags = a.getInt(index, 0);
|
||||||
final Object value = mStyleAttributes.get(index);
|
final Object value = mStyleAttributes.get(index);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
@ -147,7 +133,7 @@ public class KeyStyles {
|
||||||
return flags | parentStyle.getFlag(a, index);
|
return flags | parentStyle.getFlag(a, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void readKeyAttributes(TypedArray keyAttr) {
|
public void readKeyAttributes(final TypedArray keyAttr) {
|
||||||
// TODO: Currently not all Key attributes can be declared as style.
|
// TODO: Currently not all Key attributes can be declared as style.
|
||||||
readString(keyAttr, R.styleable.Keyboard_Key_code);
|
readString(keyAttr, R.styleable.Keyboard_Key_code);
|
||||||
readString(keyAttr, R.styleable.Keyboard_Key_altCode);
|
readString(keyAttr, R.styleable.Keyboard_Key_altCode);
|
||||||
|
@ -165,38 +151,38 @@ public class KeyStyles {
|
||||||
readFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
|
readFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readString(TypedArray a, int index) {
|
private void readString(final TypedArray a, final int index) {
|
||||||
if (a.hasValue(index)) {
|
if (a.hasValue(index)) {
|
||||||
mStyleAttributes.put(index, parseString(a, index));
|
mStyleAttributes.put(index, parseString(a, index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readInt(TypedArray a, int index) {
|
private void readInt(final TypedArray a, final int index) {
|
||||||
if (a.hasValue(index)) {
|
if (a.hasValue(index)) {
|
||||||
mStyleAttributes.put(index, a.getInt(index, 0));
|
mStyleAttributes.put(index, a.getInt(index, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readFlag(TypedArray a, int index) {
|
private void readFlag(final TypedArray a, final int index) {
|
||||||
if (a.hasValue(index)) {
|
if (a.hasValue(index)) {
|
||||||
final Integer value = (Integer)mStyleAttributes.get(index);
|
final Integer value = (Integer)mStyleAttributes.get(index);
|
||||||
mStyleAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0));
|
mStyleAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readStringArray(TypedArray a, int index) {
|
private void readStringArray(final TypedArray a, final int index) {
|
||||||
if (a.hasValue(index)) {
|
if (a.hasValue(index)) {
|
||||||
mStyleAttributes.put(index, parseStringArray(a, index));
|
mStyleAttributes.put(index, parseStringArray(a, index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs,
|
public void parseKeyStyleAttributes(final TypedArray keyStyleAttr, final TypedArray keyAttrs,
|
||||||
XmlPullParser parser) throws XmlPullParserException {
|
final XmlPullParser parser) throws XmlPullParserException {
|
||||||
final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
|
final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, String.format("<%s styleName=%s />",
|
Log.d(TAG, String.format("<%s styleName=%s />",
|
||||||
Keyboard.Builder.TAG_KEY_STYLE, styleName));
|
KeyboardBuilder.TAG_KEY_STYLE, styleName));
|
||||||
if (mStyles.containsKey(styleName)) {
|
if (mStyles.containsKey(styleName)) {
|
||||||
Log.d(TAG, "key-style " + styleName + " is overridden at "
|
Log.d(TAG, "key-style " + styleName + " is overridden at "
|
||||||
+ parser.getPositionDescription());
|
+ parser.getPositionDescription());
|
||||||
|
@ -211,12 +197,12 @@ public class KeyStyles {
|
||||||
"Unknown parentStyle " + parentStyleName, parser);
|
"Unknown parentStyle " + parentStyleName, parser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final DeclaredKeyStyle style = new DeclaredKeyStyle(parentStyleName);
|
final DeclaredKeyStyle style = new DeclaredKeyStyle(parentStyleName, mTextsSet, mStyles);
|
||||||
style.readKeyAttributes(keyAttrs);
|
style.readKeyAttributes(keyAttrs);
|
||||||
mStyles.put(styleName, style);
|
mStyles.put(styleName, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyStyle getKeyStyle(TypedArray keyAttr, XmlPullParser parser)
|
public KeyStyle getKeyStyle(final TypedArray keyAttr, final XmlPullParser parser)
|
||||||
throws XmlParseUtils.ParseException {
|
throws XmlParseUtils.ParseException {
|
||||||
if (!keyAttr.hasValue(R.styleable.Keyboard_Key_keyStyle)) {
|
if (!keyAttr.hasValue(R.styleable.Keyboard_Key_keyStyle)) {
|
||||||
return mEmptyKeyStyle;
|
return mEmptyKeyStyle;
|
|
@ -0,0 +1,829 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.inputmethod.keyboard.internal;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.content.res.XmlResourceParser;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.util.Xml;
|
||||||
|
import android.view.InflateException;
|
||||||
|
|
||||||
|
import com.android.inputmethod.keyboard.Key;
|
||||||
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
|
import com.android.inputmethod.keyboard.KeyboardId;
|
||||||
|
import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
|
||||||
|
import com.android.inputmethod.latin.R;
|
||||||
|
import com.android.inputmethod.latin.ResourceUtils;
|
||||||
|
import com.android.inputmethod.latin.StringUtils;
|
||||||
|
import com.android.inputmethod.latin.SubtypeLocale;
|
||||||
|
import com.android.inputmethod.latin.XmlParseUtils;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keyboard Building helper.
|
||||||
|
*
|
||||||
|
* This class parses Keyboard XML file and eventually build a Keyboard.
|
||||||
|
* The Keyboard XML file looks like:
|
||||||
|
* <pre>
|
||||||
|
* <!-- xml/keyboard.xml -->
|
||||||
|
* <Keyboard keyboard_attributes*>
|
||||||
|
* <!-- Keyboard Content -->
|
||||||
|
* <Row row_attributes*>
|
||||||
|
* <!-- Row Content -->
|
||||||
|
* <Key key_attributes* />
|
||||||
|
* <Spacer horizontalGap="32.0dp" />
|
||||||
|
* <include keyboardLayout="@xml/other_keys">
|
||||||
|
* ...
|
||||||
|
* </Row>
|
||||||
|
* <include keyboardLayout="@xml/other_rows">
|
||||||
|
* ...
|
||||||
|
* </Keyboard>
|
||||||
|
* </pre>
|
||||||
|
* The XML file which is included in other file must have <merge> as root element,
|
||||||
|
* such as:
|
||||||
|
* <pre>
|
||||||
|
* <!-- xml/other_keys.xml -->
|
||||||
|
* <merge>
|
||||||
|
* <Key key_attributes* />
|
||||||
|
* ...
|
||||||
|
* </merge>
|
||||||
|
* </pre>
|
||||||
|
* and
|
||||||
|
* <pre>
|
||||||
|
* <!-- xml/other_rows.xml -->
|
||||||
|
* <merge>
|
||||||
|
* <Row row_attributes*>
|
||||||
|
* <Key key_attributes* />
|
||||||
|
* </Row>
|
||||||
|
* ...
|
||||||
|
* </merge>
|
||||||
|
* </pre>
|
||||||
|
* You can also use switch-case-default tags to select Rows and Keys.
|
||||||
|
* <pre>
|
||||||
|
* <switch>
|
||||||
|
* <case case_attribute*>
|
||||||
|
* <!-- Any valid tags at switch position -->
|
||||||
|
* </case>
|
||||||
|
* ...
|
||||||
|
* <default>
|
||||||
|
* <!-- Any valid tags at switch position -->
|
||||||
|
* </default>
|
||||||
|
* </switch>
|
||||||
|
* </pre>
|
||||||
|
* You can declare Key style and specify styles within Key tags.
|
||||||
|
* <pre>
|
||||||
|
* <switch>
|
||||||
|
* <case mode="email">
|
||||||
|
* <key-style styleName="f1-key" parentStyle="modifier-key"
|
||||||
|
* keyLabel=".com"
|
||||||
|
* />
|
||||||
|
* </case>
|
||||||
|
* <case mode="url">
|
||||||
|
* <key-style styleName="f1-key" parentStyle="modifier-key"
|
||||||
|
* keyLabel="http://"
|
||||||
|
* />
|
||||||
|
* </case>
|
||||||
|
* </switch>
|
||||||
|
* ...
|
||||||
|
* <Key keyStyle="shift-key" ... />
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class KeyboardBuilder<KP extends KeyboardParams> {
|
||||||
|
private static final String BUILDER_TAG = "Keyboard.Builder";
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
// Keyboard XML Tags
|
||||||
|
private static final String TAG_KEYBOARD = "Keyboard";
|
||||||
|
private static final String TAG_ROW = "Row";
|
||||||
|
private static final String TAG_KEY = "Key";
|
||||||
|
private static final String TAG_SPACER = "Spacer";
|
||||||
|
private static final String TAG_INCLUDE = "include";
|
||||||
|
private static final String TAG_MERGE = "merge";
|
||||||
|
private static final String TAG_SWITCH = "switch";
|
||||||
|
private static final String TAG_CASE = "case";
|
||||||
|
private static final String TAG_DEFAULT = "default";
|
||||||
|
public static final String TAG_KEY_STYLE = "key-style";
|
||||||
|
|
||||||
|
private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
|
||||||
|
private static final int DEFAULT_KEYBOARD_ROWS = 4;
|
||||||
|
|
||||||
|
protected final KP mParams;
|
||||||
|
protected final Context mContext;
|
||||||
|
protected final Resources mResources;
|
||||||
|
private final DisplayMetrics mDisplayMetrics;
|
||||||
|
|
||||||
|
private int mCurrentY = 0;
|
||||||
|
private KeyboardRow mCurrentRow = null;
|
||||||
|
private boolean mLeftEdge;
|
||||||
|
private boolean mTopEdge;
|
||||||
|
private Key mRightEdgeKey = null;
|
||||||
|
|
||||||
|
public KeyboardBuilder(final Context context, final KP params) {
|
||||||
|
mContext = context;
|
||||||
|
final Resources res = context.getResources();
|
||||||
|
mResources = res;
|
||||||
|
mDisplayMetrics = res.getDisplayMetrics();
|
||||||
|
|
||||||
|
mParams = params;
|
||||||
|
|
||||||
|
params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
|
||||||
|
params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAutoGenerate(final KeysCache keysCache) {
|
||||||
|
mParams.mKeysCache = keysCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyboardBuilder<KP> load(final int xmlId, final KeyboardId id) {
|
||||||
|
mParams.mId = id;
|
||||||
|
final XmlResourceParser parser = mResources.getXml(xmlId);
|
||||||
|
try {
|
||||||
|
parseKeyboard(parser);
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
parser.close();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this method.
|
||||||
|
public void setTouchPositionCorrectionEnabled(final boolean enabled) {
|
||||||
|
mParams.mTouchPositionCorrection.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProximityCharsCorrectionEnabled(final boolean enabled) {
|
||||||
|
mParams.mProximityCharsCorrectionEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Keyboard build() {
|
||||||
|
return new Keyboard(mParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int mIndent;
|
||||||
|
private static final String SPACES = " ";
|
||||||
|
|
||||||
|
private static String spaces(final int count) {
|
||||||
|
return (count < SPACES.length()) ? SPACES.substring(0, count) : SPACES;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startTag(final String format, final Object ... args) {
|
||||||
|
Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void endTag(final String format, final Object ... args) {
|
||||||
|
Log.d(BUILDER_TAG, String.format(spaces(mIndent-- * 2) + format, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startEndTag(final String format, final Object ... args) {
|
||||||
|
Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
|
||||||
|
mIndent--;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseKeyboard(final XmlPullParser parser)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId);
|
||||||
|
int event;
|
||||||
|
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (event == XmlPullParser.START_TAG) {
|
||||||
|
final String tag = parser.getName();
|
||||||
|
if (TAG_KEYBOARD.equals(tag)) {
|
||||||
|
parseKeyboardAttributes(parser);
|
||||||
|
startKeyboard();
|
||||||
|
parseKeyboardContent(parser, false);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseKeyboardAttributes(final XmlPullParser parser) {
|
||||||
|
final int displayWidth = mDisplayMetrics.widthPixels;
|
||||||
|
final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
|
||||||
|
Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
|
||||||
|
R.style.Keyboard);
|
||||||
|
final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.Keyboard_Key);
|
||||||
|
final TypedArray keyboardViewAttr = mResources.obtainAttributes(
|
||||||
|
Xml.asAttributeSet(parser), R.styleable.KeyboardView);
|
||||||
|
try {
|
||||||
|
final int displayHeight = mDisplayMetrics.heightPixels;
|
||||||
|
final String keyboardHeightString = ResourceUtils.getDeviceOverrideValue(
|
||||||
|
mResources, R.array.keyboard_heights, null);
|
||||||
|
final float keyboardHeight;
|
||||||
|
if (keyboardHeightString != null) {
|
||||||
|
keyboardHeight = Float.parseFloat(keyboardHeightString)
|
||||||
|
* mDisplayMetrics.density;
|
||||||
|
} else {
|
||||||
|
keyboardHeight = keyboardAttr.getDimension(
|
||||||
|
R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
|
||||||
|
}
|
||||||
|
final float maxKeyboardHeight = ResourceUtils.getDimensionOrFraction(keyboardAttr,
|
||||||
|
R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
|
||||||
|
float minKeyboardHeight = ResourceUtils.getDimensionOrFraction(keyboardAttr,
|
||||||
|
R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2);
|
||||||
|
if (minKeyboardHeight < 0) {
|
||||||
|
// Specified fraction was negative, so it should be calculated against display
|
||||||
|
// width.
|
||||||
|
minKeyboardHeight = -ResourceUtils.getDimensionOrFraction(keyboardAttr,
|
||||||
|
R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
|
||||||
|
}
|
||||||
|
final KeyboardParams params = mParams;
|
||||||
|
// Keyboard height will not exceed maxKeyboardHeight and will not be less than
|
||||||
|
// minKeyboardHeight.
|
||||||
|
params.mOccupiedHeight = (int)Math.max(
|
||||||
|
Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
|
||||||
|
params.mOccupiedWidth = params.mId.mWidth;
|
||||||
|
params.mTopPadding = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
|
||||||
|
R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0);
|
||||||
|
params.mBottomPadding = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
|
||||||
|
R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0);
|
||||||
|
params.mHorizontalEdgesPadding = (int)ResourceUtils.getDimensionOrFraction(
|
||||||
|
keyboardAttr,
|
||||||
|
R.styleable.Keyboard_keyboardHorizontalEdgesPadding,
|
||||||
|
mParams.mOccupiedWidth, 0);
|
||||||
|
|
||||||
|
params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2
|
||||||
|
- params.mHorizontalCenterPadding;
|
||||||
|
params.mDefaultKeyWidth = (int)ResourceUtils.getDimensionOrFraction(keyAttr,
|
||||||
|
R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth,
|
||||||
|
params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS);
|
||||||
|
params.mHorizontalGap = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
|
||||||
|
R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0);
|
||||||
|
params.mVerticalGap = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
|
||||||
|
R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0);
|
||||||
|
params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding
|
||||||
|
- params.mBottomPadding + params.mVerticalGap;
|
||||||
|
params.mDefaultRowHeight = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
|
||||||
|
R.styleable.Keyboard_rowHeight, params.mBaseHeight,
|
||||||
|
params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
|
||||||
|
|
||||||
|
if (keyboardViewAttr.hasValue(R.styleable.KeyboardView_keyTypeface)) {
|
||||||
|
params.mKeyTypeface = Typeface.defaultFromStyle(keyboardViewAttr.getInt(
|
||||||
|
R.styleable.KeyboardView_keyTypeface, Typeface.NORMAL));
|
||||||
|
}
|
||||||
|
params.mKeyLetterRatio = ResourceUtils.getFraction(keyboardViewAttr,
|
||||||
|
R.styleable.KeyboardView_keyLetterSize);
|
||||||
|
params.mKeyLetterSize = ResourceUtils.getDimensionPixelSize(keyboardViewAttr,
|
||||||
|
R.styleable.KeyboardView_keyLetterSize);
|
||||||
|
params.mKeyHintLetterRatio = ResourceUtils.getFraction(keyboardViewAttr,
|
||||||
|
R.styleable.KeyboardView_keyHintLetterRatio);
|
||||||
|
params.mKeyShiftedLetterHintRatio = ResourceUtils.getFraction(keyboardViewAttr,
|
||||||
|
R.styleable.KeyboardView_keyShiftedLetterHintRatio);
|
||||||
|
|
||||||
|
params.mMoreKeysTemplate = keyboardAttr.getResourceId(
|
||||||
|
R.styleable.Keyboard_moreKeysTemplate, 0);
|
||||||
|
params.mMaxMoreKeysKeyboardColumn = keyAttr.getInt(
|
||||||
|
R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
|
||||||
|
|
||||||
|
params.mThemeId = keyboardAttr.getInt(R.styleable.Keyboard_themeId, 0);
|
||||||
|
params.mIconsSet.loadIcons(keyboardAttr);
|
||||||
|
final String language = params.mId.mLocale.getLanguage();
|
||||||
|
params.mCodesSet.setLanguage(language);
|
||||||
|
params.mTextsSet.setLanguage(language);
|
||||||
|
final RunInLocale<Void> job = new RunInLocale<Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void job(Resources res) {
|
||||||
|
params.mTextsSet.loadStringResources(mContext);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Null means the current system locale.
|
||||||
|
final Locale locale = SubtypeLocale.isNoLanguage(params.mId.mSubtype)
|
||||||
|
? null : params.mId.mLocale;
|
||||||
|
job.runInLocale(mResources, locale);
|
||||||
|
|
||||||
|
final int resourceId = keyboardAttr.getResourceId(
|
||||||
|
R.styleable.Keyboard_touchPositionCorrectionData, 0);
|
||||||
|
params.mTouchPositionCorrection.setEnabled(resourceId != 0);
|
||||||
|
if (resourceId != 0) {
|
||||||
|
final String[] data = mResources.getStringArray(resourceId);
|
||||||
|
params.mTouchPositionCorrection.load(data);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
keyboardViewAttr.recycle();
|
||||||
|
keyAttr.recycle();
|
||||||
|
keyboardAttr.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseKeyboardContent(final XmlPullParser parser, final boolean skip)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
int event;
|
||||||
|
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (event == XmlPullParser.START_TAG) {
|
||||||
|
final String tag = parser.getName();
|
||||||
|
if (TAG_ROW.equals(tag)) {
|
||||||
|
final KeyboardRow row = parseRowAttributes(parser);
|
||||||
|
if (DEBUG) startTag("<%s>%s", TAG_ROW, skip ? " skipped" : "");
|
||||||
|
if (!skip) {
|
||||||
|
startRow(row);
|
||||||
|
}
|
||||||
|
parseRowContent(parser, row, skip);
|
||||||
|
} else if (TAG_INCLUDE.equals(tag)) {
|
||||||
|
parseIncludeKeyboardContent(parser, skip);
|
||||||
|
} else if (TAG_SWITCH.equals(tag)) {
|
||||||
|
parseSwitchKeyboardContent(parser, skip);
|
||||||
|
} else if (TAG_KEY_STYLE.equals(tag)) {
|
||||||
|
parseKeyStyle(parser, skip);
|
||||||
|
} else {
|
||||||
|
throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW);
|
||||||
|
}
|
||||||
|
} else if (event == XmlPullParser.END_TAG) {
|
||||||
|
final String tag = parser.getName();
|
||||||
|
if (DEBUG) endTag("</%s>", tag);
|
||||||
|
if (TAG_KEYBOARD.equals(tag)) {
|
||||||
|
endKeyboard();
|
||||||
|
break;
|
||||||
|
} else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
|
||||||
|
|| TAG_MERGE.equals(tag)) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyboardRow parseRowAttributes(final XmlPullParser parser)
|
||||||
|
throws XmlPullParserException {
|
||||||
|
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.Keyboard);
|
||||||
|
try {
|
||||||
|
if (a.hasValue(R.styleable.Keyboard_horizontalGap)) {
|
||||||
|
throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
|
||||||
|
}
|
||||||
|
if (a.hasValue(R.styleable.Keyboard_verticalGap)) {
|
||||||
|
throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
|
||||||
|
}
|
||||||
|
return new KeyboardRow(mResources, mParams, parser, mCurrentY);
|
||||||
|
} finally {
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseRowContent(final XmlPullParser parser, final KeyboardRow row,
|
||||||
|
final boolean skip) throws XmlPullParserException, IOException {
|
||||||
|
int event;
|
||||||
|
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (event == XmlPullParser.START_TAG) {
|
||||||
|
final String tag = parser.getName();
|
||||||
|
if (TAG_KEY.equals(tag)) {
|
||||||
|
parseKey(parser, row, skip);
|
||||||
|
} else if (TAG_SPACER.equals(tag)) {
|
||||||
|
parseSpacer(parser, row, skip);
|
||||||
|
} else if (TAG_INCLUDE.equals(tag)) {
|
||||||
|
parseIncludeRowContent(parser, row, skip);
|
||||||
|
} else if (TAG_SWITCH.equals(tag)) {
|
||||||
|
parseSwitchRowContent(parser, row, skip);
|
||||||
|
} else if (TAG_KEY_STYLE.equals(tag)) {
|
||||||
|
parseKeyStyle(parser, skip);
|
||||||
|
} else {
|
||||||
|
throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
|
||||||
|
}
|
||||||
|
} else if (event == XmlPullParser.END_TAG) {
|
||||||
|
final String tag = parser.getName();
|
||||||
|
if (DEBUG) endTag("</%s>", tag);
|
||||||
|
if (TAG_ROW.equals(tag)) {
|
||||||
|
if (!skip) {
|
||||||
|
endRow(row);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
|
||||||
|
|| TAG_MERGE.equals(tag)) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseKey(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
if (skip) {
|
||||||
|
XmlParseUtils.checkEndTag(TAG_KEY, parser);
|
||||||
|
if (DEBUG) {
|
||||||
|
startEndTag("<%s /> skipped", TAG_KEY);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final Key key = new Key(mResources, mParams, row, parser);
|
||||||
|
if (DEBUG) {
|
||||||
|
startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY,
|
||||||
|
(key.isEnabled() ? "" : " disabled"), key,
|
||||||
|
Arrays.toString(key.mMoreKeys));
|
||||||
|
}
|
||||||
|
XmlParseUtils.checkEndTag(TAG_KEY, parser);
|
||||||
|
endKey(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseSpacer(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
if (skip) {
|
||||||
|
XmlParseUtils.checkEndTag(TAG_SPACER, parser);
|
||||||
|
if (DEBUG) startEndTag("<%s /> skipped", TAG_SPACER);
|
||||||
|
} else {
|
||||||
|
final Key.Spacer spacer = new Key.Spacer(mResources, mParams, row, parser);
|
||||||
|
if (DEBUG) startEndTag("<%s />", TAG_SPACER);
|
||||||
|
XmlParseUtils.checkEndTag(TAG_SPACER, parser);
|
||||||
|
endKey(spacer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseIncludeKeyboardContent(final XmlPullParser parser, final boolean skip)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
parseIncludeInternal(parser, null, skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseIncludeRowContent(final XmlPullParser parser, final KeyboardRow row,
|
||||||
|
final boolean skip) throws XmlPullParserException, IOException {
|
||||||
|
parseIncludeInternal(parser, row, skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseIncludeInternal(final XmlPullParser parser, final KeyboardRow row,
|
||||||
|
final boolean skip) throws XmlPullParserException, IOException {
|
||||||
|
if (skip) {
|
||||||
|
XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
|
||||||
|
if (DEBUG) startEndTag("</%s> skipped", TAG_INCLUDE);
|
||||||
|
} else {
|
||||||
|
final AttributeSet attr = Xml.asAttributeSet(parser);
|
||||||
|
final TypedArray keyboardAttr = mResources.obtainAttributes(attr,
|
||||||
|
R.styleable.Keyboard_Include);
|
||||||
|
final TypedArray keyAttr = mResources.obtainAttributes(attr,
|
||||||
|
R.styleable.Keyboard_Key);
|
||||||
|
int keyboardLayout = 0;
|
||||||
|
float savedDefaultKeyWidth = 0;
|
||||||
|
int savedDefaultKeyLabelFlags = 0;
|
||||||
|
int savedDefaultBackgroundType = Key.BACKGROUND_TYPE_NORMAL;
|
||||||
|
try {
|
||||||
|
XmlParseUtils.checkAttributeExists(keyboardAttr,
|
||||||
|
R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
|
||||||
|
TAG_INCLUDE, parser);
|
||||||
|
keyboardLayout = keyboardAttr.getResourceId(
|
||||||
|
R.styleable.Keyboard_Include_keyboardLayout, 0);
|
||||||
|
if (row != null) {
|
||||||
|
if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
|
||||||
|
// Override current x coordinate.
|
||||||
|
row.setXPos(row.getKeyX(keyAttr));
|
||||||
|
}
|
||||||
|
// TODO: Remove this if-clause and do the same as backgroundType below.
|
||||||
|
savedDefaultKeyWidth = row.getDefaultKeyWidth();
|
||||||
|
if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyWidth)) {
|
||||||
|
// Override default key width.
|
||||||
|
row.setDefaultKeyWidth(row.getKeyWidth(keyAttr));
|
||||||
|
}
|
||||||
|
savedDefaultKeyLabelFlags = row.getDefaultKeyLabelFlags();
|
||||||
|
// Bitwise-or default keyLabelFlag if exists.
|
||||||
|
row.setDefaultKeyLabelFlags(keyAttr.getInt(
|
||||||
|
R.styleable.Keyboard_Key_keyLabelFlags, 0)
|
||||||
|
| savedDefaultKeyLabelFlags);
|
||||||
|
savedDefaultBackgroundType = row.getDefaultBackgroundType();
|
||||||
|
// Override default backgroundType if exists.
|
||||||
|
row.setDefaultBackgroundType(keyAttr.getInt(
|
||||||
|
R.styleable.Keyboard_Key_backgroundType,
|
||||||
|
savedDefaultBackgroundType));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
keyboardAttr.recycle();
|
||||||
|
keyAttr.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
|
||||||
|
if (DEBUG) {
|
||||||
|
startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE,
|
||||||
|
mResources.getResourceEntryName(keyboardLayout));
|
||||||
|
}
|
||||||
|
final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
|
||||||
|
try {
|
||||||
|
parseMerge(parserForInclude, row, skip);
|
||||||
|
} finally {
|
||||||
|
if (row != null) {
|
||||||
|
// Restore default keyWidth, keyLabelFlags, and backgroundType.
|
||||||
|
row.setDefaultKeyWidth(savedDefaultKeyWidth);
|
||||||
|
row.setDefaultKeyLabelFlags(savedDefaultKeyLabelFlags);
|
||||||
|
row.setDefaultBackgroundType(savedDefaultBackgroundType);
|
||||||
|
}
|
||||||
|
parserForInclude.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseMerge(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
if (DEBUG) startTag("<%s>", TAG_MERGE);
|
||||||
|
int event;
|
||||||
|
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (event == XmlPullParser.START_TAG) {
|
||||||
|
final String tag = parser.getName();
|
||||||
|
if (TAG_MERGE.equals(tag)) {
|
||||||
|
if (row == null) {
|
||||||
|
parseKeyboardContent(parser, skip);
|
||||||
|
} else {
|
||||||
|
parseRowContent(parser, row, skip);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw new XmlParseUtils.ParseException(
|
||||||
|
"Included keyboard layout must have <merge> root element", parser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseSwitchKeyboardContent(final XmlPullParser parser, final boolean skip)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
parseSwitchInternal(parser, null, skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseSwitchRowContent(final XmlPullParser parser, final KeyboardRow row,
|
||||||
|
final boolean skip) throws XmlPullParserException, IOException {
|
||||||
|
parseSwitchInternal(parser, row, skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseSwitchInternal(final XmlPullParser parser, final KeyboardRow row,
|
||||||
|
final boolean skip) throws XmlPullParserException, IOException {
|
||||||
|
if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId);
|
||||||
|
boolean selected = false;
|
||||||
|
int event;
|
||||||
|
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (event == XmlPullParser.START_TAG) {
|
||||||
|
final String tag = parser.getName();
|
||||||
|
if (TAG_CASE.equals(tag)) {
|
||||||
|
selected |= parseCase(parser, row, selected ? true : skip);
|
||||||
|
} else if (TAG_DEFAULT.equals(tag)) {
|
||||||
|
selected |= parseDefault(parser, row, selected ? true : skip);
|
||||||
|
} else {
|
||||||
|
throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
|
||||||
|
}
|
||||||
|
} else if (event == XmlPullParser.END_TAG) {
|
||||||
|
final String tag = parser.getName();
|
||||||
|
if (TAG_SWITCH.equals(tag)) {
|
||||||
|
if (DEBUG) endTag("</%s>", TAG_SWITCH);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean parseCase(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
final boolean selected = parseCaseCondition(parser);
|
||||||
|
if (row == null) {
|
||||||
|
// Processing Rows.
|
||||||
|
parseKeyboardContent(parser, selected ? skip : true);
|
||||||
|
} else {
|
||||||
|
// Processing Keys.
|
||||||
|
parseRowContent(parser, row, selected ? skip : true);
|
||||||
|
}
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean parseCaseCondition(final XmlPullParser parser) {
|
||||||
|
final KeyboardId id = mParams.mId;
|
||||||
|
if (id == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.Keyboard_Case);
|
||||||
|
try {
|
||||||
|
final boolean keyboardLayoutSetElementMatched = matchTypedValue(a,
|
||||||
|
R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId,
|
||||||
|
KeyboardId.elementIdToName(id.mElementId));
|
||||||
|
final boolean modeMatched = matchTypedValue(a,
|
||||||
|
R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
|
||||||
|
final boolean navigateNextMatched = matchBoolean(a,
|
||||||
|
R.styleable.Keyboard_Case_navigateNext, id.navigateNext());
|
||||||
|
final boolean navigatePreviousMatched = matchBoolean(a,
|
||||||
|
R.styleable.Keyboard_Case_navigatePrevious, id.navigatePrevious());
|
||||||
|
final boolean passwordInputMatched = matchBoolean(a,
|
||||||
|
R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
|
||||||
|
final boolean clobberSettingsKeyMatched = matchBoolean(a,
|
||||||
|
R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
|
||||||
|
final boolean shortcutKeyEnabledMatched = matchBoolean(a,
|
||||||
|
R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
|
||||||
|
final boolean hasShortcutKeyMatched = matchBoolean(a,
|
||||||
|
R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
|
||||||
|
final boolean languageSwitchKeyEnabledMatched = matchBoolean(a,
|
||||||
|
R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
|
||||||
|
id.mLanguageSwitchKeyEnabled);
|
||||||
|
final boolean isMultiLineMatched = matchBoolean(a,
|
||||||
|
R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine());
|
||||||
|
final boolean imeActionMatched = matchInteger(a,
|
||||||
|
R.styleable.Keyboard_Case_imeAction, id.imeAction());
|
||||||
|
final boolean localeCodeMatched = matchString(a,
|
||||||
|
R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
|
||||||
|
final boolean languageCodeMatched = matchString(a,
|
||||||
|
R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
|
||||||
|
final boolean countryCodeMatched = matchString(a,
|
||||||
|
R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
|
||||||
|
final boolean selected = keyboardLayoutSetElementMatched && modeMatched
|
||||||
|
&& navigateNextMatched && navigatePreviousMatched && passwordInputMatched
|
||||||
|
&& clobberSettingsKeyMatched && shortcutKeyEnabledMatched
|
||||||
|
&& hasShortcutKeyMatched && languageSwitchKeyEnabledMatched
|
||||||
|
&& isMultiLineMatched && imeActionMatched && localeCodeMatched
|
||||||
|
&& languageCodeMatched && countryCodeMatched;
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
|
||||||
|
textAttr(a.getString(
|
||||||
|
R.styleable.Keyboard_Case_keyboardLayoutSetElement),
|
||||||
|
"keyboardLayoutSetElement"),
|
||||||
|
textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
|
||||||
|
textAttr(a.getString(R.styleable.Keyboard_Case_imeAction),
|
||||||
|
"imeAction"),
|
||||||
|
booleanAttr(a, R.styleable.Keyboard_Case_navigateNext,
|
||||||
|
"navigateNext"),
|
||||||
|
booleanAttr(a, R.styleable.Keyboard_Case_navigatePrevious,
|
||||||
|
"navigatePrevious"),
|
||||||
|
booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
|
||||||
|
"clobberSettingsKey"),
|
||||||
|
booleanAttr(a, R.styleable.Keyboard_Case_passwordInput,
|
||||||
|
"passwordInput"),
|
||||||
|
booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
|
||||||
|
"shortcutKeyEnabled"),
|
||||||
|
booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey,
|
||||||
|
"hasShortcutKey"),
|
||||||
|
booleanAttr(a, R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
|
||||||
|
"languageSwitchKeyEnabled"),
|
||||||
|
booleanAttr(a, R.styleable.Keyboard_Case_isMultiLine,
|
||||||
|
"isMultiLine"),
|
||||||
|
textAttr(a.getString(R.styleable.Keyboard_Case_localeCode),
|
||||||
|
"localeCode"),
|
||||||
|
textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
|
||||||
|
"languageCode"),
|
||||||
|
textAttr(a.getString(R.styleable.Keyboard_Case_countryCode),
|
||||||
|
"countryCode"),
|
||||||
|
selected ? "" : " skipped");
|
||||||
|
}
|
||||||
|
|
||||||
|
return selected;
|
||||||
|
} finally {
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean matchInteger(final TypedArray a, final int index, final int value) {
|
||||||
|
// If <case> does not have "index" attribute, that means this <case> is wild-card for
|
||||||
|
// the attribute.
|
||||||
|
return !a.hasValue(index) || a.getInt(index, 0) == value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean matchBoolean(final TypedArray a, final int index, final boolean value) {
|
||||||
|
// If <case> does not have "index" attribute, that means this <case> is wild-card for
|
||||||
|
// the attribute.
|
||||||
|
return !a.hasValue(index) || a.getBoolean(index, false) == value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean matchString(final TypedArray a, final int index, final String value) {
|
||||||
|
// If <case> does not have "index" attribute, that means this <case> is wild-card for
|
||||||
|
// the attribute.
|
||||||
|
return !a.hasValue(index)
|
||||||
|
|| StringUtils.containsInArray(value, a.getString(index).split("\\|"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean matchTypedValue(final TypedArray a, final int index, final int intValue,
|
||||||
|
final String strValue) {
|
||||||
|
// If <case> does not have "index" attribute, that means this <case> is wild-card for
|
||||||
|
// the attribute.
|
||||||
|
final TypedValue v = a.peekValue(index);
|
||||||
|
if (v == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ResourceUtils.isIntegerValue(v)) {
|
||||||
|
return intValue == a.getInt(index, 0);
|
||||||
|
} else if (ResourceUtils.isStringValue(v)) {
|
||||||
|
return StringUtils.containsInArray(strValue, a.getString(index).split("\\|"));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean parseDefault(final XmlPullParser parser, final KeyboardRow row,
|
||||||
|
final boolean skip) throws XmlPullParserException, IOException {
|
||||||
|
if (DEBUG) startTag("<%s>", TAG_DEFAULT);
|
||||||
|
if (row == null) {
|
||||||
|
parseKeyboardContent(parser, skip);
|
||||||
|
} else {
|
||||||
|
parseRowContent(parser, row, skip);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseKeyStyle(final XmlPullParser parser, final boolean skip)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.Keyboard_KeyStyle);
|
||||||
|
TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.Keyboard_Key);
|
||||||
|
try {
|
||||||
|
if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) {
|
||||||
|
throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
|
||||||
|
+ "/> needs styleName attribute", parser);
|
||||||
|
}
|
||||||
|
if (DEBUG) {
|
||||||
|
startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE,
|
||||||
|
keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
|
||||||
|
skip ? " skipped" : "");
|
||||||
|
}
|
||||||
|
if (!skip) {
|
||||||
|
mParams.mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
keyStyleAttr.recycle();
|
||||||
|
keyAttrs.recycle();
|
||||||
|
}
|
||||||
|
XmlParseUtils.checkEndTag(TAG_KEY_STYLE, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startKeyboard() {
|
||||||
|
mCurrentY += mParams.mTopPadding;
|
||||||
|
mTopEdge = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startRow(final KeyboardRow row) {
|
||||||
|
addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
|
||||||
|
mCurrentRow = row;
|
||||||
|
mLeftEdge = true;
|
||||||
|
mRightEdgeKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void endRow(final KeyboardRow row) {
|
||||||
|
if (mCurrentRow == null) {
|
||||||
|
throw new InflateException("orphan end row tag");
|
||||||
|
}
|
||||||
|
if (mRightEdgeKey != null) {
|
||||||
|
mRightEdgeKey.markAsRightEdge(mParams);
|
||||||
|
mRightEdgeKey = null;
|
||||||
|
}
|
||||||
|
addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
|
||||||
|
mCurrentY += row.mRowHeight;
|
||||||
|
mCurrentRow = null;
|
||||||
|
mTopEdge = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void endKey(final Key key) {
|
||||||
|
mParams.onAddKey(key);
|
||||||
|
if (mLeftEdge) {
|
||||||
|
key.markAsLeftEdge(mParams);
|
||||||
|
mLeftEdge = false;
|
||||||
|
}
|
||||||
|
if (mTopEdge) {
|
||||||
|
key.markAsTopEdge(mParams);
|
||||||
|
}
|
||||||
|
mRightEdgeKey = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void endKeyboard() {
|
||||||
|
// nothing to do here.
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEdgeSpace(final float width, final KeyboardRow row) {
|
||||||
|
row.advanceXPos(width);
|
||||||
|
mLeftEdge = false;
|
||||||
|
mRightEdgeKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String textAttr(final String value, final String name) {
|
||||||
|
return value != null ? String.format(" %s=%s", name, value) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String booleanAttr(final TypedArray a, final int index, final String name) {
|
||||||
|
return a.hasValue(index)
|
||||||
|
? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.inputmethod.keyboard.internal;
|
||||||
|
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
|
import com.android.inputmethod.keyboard.Key;
|
||||||
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
|
import com.android.inputmethod.keyboard.KeyboardId;
|
||||||
|
import com.android.inputmethod.latin.CollectionUtils;
|
||||||
|
import com.android.inputmethod.latin.ResourceUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
public class KeyboardParams {
|
||||||
|
public KeyboardId mId;
|
||||||
|
public int mThemeId;
|
||||||
|
|
||||||
|
/** Total height and width of the keyboard, including the paddings and keys */
|
||||||
|
public int mOccupiedHeight;
|
||||||
|
public int mOccupiedWidth;
|
||||||
|
|
||||||
|
/** Base height and width of the keyboard used to calculate rows' or keys' heights and
|
||||||
|
* widths
|
||||||
|
*/
|
||||||
|
public int mBaseHeight;
|
||||||
|
public int mBaseWidth;
|
||||||
|
|
||||||
|
public int mTopPadding;
|
||||||
|
public int mBottomPadding;
|
||||||
|
public int mHorizontalEdgesPadding;
|
||||||
|
public int mHorizontalCenterPadding;
|
||||||
|
|
||||||
|
public Typeface mKeyTypeface = null;
|
||||||
|
public float mKeyLetterRatio = ResourceUtils.UNDEFINED_RATIO;
|
||||||
|
public int mKeyLetterSize = ResourceUtils.UNDEFINED_DIMENSION;
|
||||||
|
public float mKeyHintLetterRatio = ResourceUtils.UNDEFINED_RATIO;
|
||||||
|
public float mKeyShiftedLetterHintRatio = ResourceUtils.UNDEFINED_RATIO;
|
||||||
|
|
||||||
|
public int mDefaultRowHeight;
|
||||||
|
public int mDefaultKeyWidth;
|
||||||
|
public int mHorizontalGap;
|
||||||
|
public int mVerticalGap;
|
||||||
|
|
||||||
|
public int mMoreKeysTemplate;
|
||||||
|
public int mMaxMoreKeysKeyboardColumn;
|
||||||
|
|
||||||
|
public int GRID_WIDTH;
|
||||||
|
public int GRID_HEIGHT;
|
||||||
|
|
||||||
|
public final HashSet<Key> mKeys = CollectionUtils.newHashSet();
|
||||||
|
public final ArrayList<Key> mShiftKeys = CollectionUtils.newArrayList();
|
||||||
|
public final ArrayList<Key> mAltCodeKeysWhileTyping = CollectionUtils.newArrayList();
|
||||||
|
public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
|
||||||
|
public final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
|
||||||
|
public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
|
||||||
|
public final KeyStylesSet mKeyStyles = new KeyStylesSet(mTextsSet);
|
||||||
|
|
||||||
|
public KeysCache mKeysCache;
|
||||||
|
|
||||||
|
public int mMostCommonKeyHeight = 0;
|
||||||
|
public int mMostCommonKeyWidth = 0;
|
||||||
|
|
||||||
|
public boolean mProximityCharsCorrectionEnabled;
|
||||||
|
|
||||||
|
public final TouchPositionCorrection mTouchPositionCorrection =
|
||||||
|
new TouchPositionCorrection();
|
||||||
|
|
||||||
|
protected void clearKeys() {
|
||||||
|
mKeys.clear();
|
||||||
|
mShiftKeys.clear();
|
||||||
|
clearHistogram();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAddKey(final Key newKey) {
|
||||||
|
final Key key = (mKeysCache != null) ? mKeysCache.get(newKey) : newKey;
|
||||||
|
final boolean zeroWidthSpacer = key.isSpacer() && key.mWidth == 0;
|
||||||
|
if (!zeroWidthSpacer) {
|
||||||
|
mKeys.add(key);
|
||||||
|
updateHistogram(key);
|
||||||
|
}
|
||||||
|
if (key.mCode == Keyboard.CODE_SHIFT) {
|
||||||
|
mShiftKeys.add(key);
|
||||||
|
}
|
||||||
|
if (key.altCodeWhileTyping()) {
|
||||||
|
mAltCodeKeysWhileTyping.add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int mMaxHeightCount = 0;
|
||||||
|
private int mMaxWidthCount = 0;
|
||||||
|
private final SparseIntArray mHeightHistogram = new SparseIntArray();
|
||||||
|
private final SparseIntArray mWidthHistogram = new SparseIntArray();
|
||||||
|
|
||||||
|
private void clearHistogram() {
|
||||||
|
mMostCommonKeyHeight = 0;
|
||||||
|
mMaxHeightCount = 0;
|
||||||
|
mHeightHistogram.clear();
|
||||||
|
|
||||||
|
mMaxWidthCount = 0;
|
||||||
|
mMostCommonKeyWidth = 0;
|
||||||
|
mWidthHistogram.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int updateHistogramCounter(final SparseIntArray histogram, final int key) {
|
||||||
|
final int index = histogram.indexOfKey(key);
|
||||||
|
final int count = (index >= 0 ? histogram.get(key) : 0) + 1;
|
||||||
|
histogram.put(key, count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateHistogram(final Key key) {
|
||||||
|
final int height = key.mHeight + mVerticalGap;
|
||||||
|
final int heightCount = updateHistogramCounter(mHeightHistogram, height);
|
||||||
|
if (heightCount > mMaxHeightCount) {
|
||||||
|
mMaxHeightCount = heightCount;
|
||||||
|
mMostCommonKeyHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int width = key.mWidth + mHorizontalGap;
|
||||||
|
final int widthCount = updateHistogramCounter(mWidthHistogram, width);
|
||||||
|
if (widthCount > mMaxWidthCount) {
|
||||||
|
mMaxWidthCount = widthCount;
|
||||||
|
mMostCommonKeyWidth = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.inputmethod.keyboard.internal;
|
||||||
|
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.util.Xml;
|
||||||
|
|
||||||
|
import com.android.inputmethod.keyboard.Key;
|
||||||
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
|
import com.android.inputmethod.latin.R;
|
||||||
|
import com.android.inputmethod.latin.ResourceUtils;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
|
||||||
|
* Some of the key size defaults can be overridden per row from what the {@link Keyboard}
|
||||||
|
* defines.
|
||||||
|
*/
|
||||||
|
public class KeyboardRow {
|
||||||
|
// keyWidth enum constants
|
||||||
|
private static final int KEYWIDTH_NOT_ENUM = 0;
|
||||||
|
private static final int KEYWIDTH_FILL_RIGHT = -1;
|
||||||
|
|
||||||
|
private final KeyboardParams mParams;
|
||||||
|
/** Default width of a key in this row. */
|
||||||
|
private float mDefaultKeyWidth;
|
||||||
|
/** Default height of a key in this row. */
|
||||||
|
public final int mRowHeight;
|
||||||
|
/** Default keyLabelFlags in this row. */
|
||||||
|
private int mDefaultKeyLabelFlags;
|
||||||
|
/** Default backgroundType for this row */
|
||||||
|
private int mDefaultBackgroundType;
|
||||||
|
|
||||||
|
private final int mCurrentY;
|
||||||
|
// Will be updated by {@link Key}'s constructor.
|
||||||
|
private float mCurrentX;
|
||||||
|
|
||||||
|
public KeyboardRow(final Resources res, final KeyboardParams params, final XmlPullParser parser,
|
||||||
|
final int y) {
|
||||||
|
mParams = params;
|
||||||
|
TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.Keyboard);
|
||||||
|
mRowHeight = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
|
||||||
|
R.styleable.Keyboard_rowHeight,
|
||||||
|
params.mBaseHeight, params.mDefaultRowHeight);
|
||||||
|
keyboardAttr.recycle();
|
||||||
|
TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
|
||||||
|
R.styleable.Keyboard_Key);
|
||||||
|
mDefaultKeyWidth = ResourceUtils.getDimensionOrFraction(keyAttr,
|
||||||
|
R.styleable.Keyboard_Key_keyWidth,
|
||||||
|
params.mBaseWidth, params.mDefaultKeyWidth);
|
||||||
|
mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
|
||||||
|
Key.BACKGROUND_TYPE_NORMAL);
|
||||||
|
keyAttr.recycle();
|
||||||
|
|
||||||
|
// TODO: Initialize this with <Row> attribute as backgroundType is done.
|
||||||
|
mDefaultKeyLabelFlags = 0;
|
||||||
|
mCurrentY = y;
|
||||||
|
mCurrentX = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDefaultKeyWidth() {
|
||||||
|
return mDefaultKeyWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultKeyWidth(final float defaultKeyWidth) {
|
||||||
|
mDefaultKeyWidth = defaultKeyWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDefaultKeyLabelFlags() {
|
||||||
|
return mDefaultKeyLabelFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultKeyLabelFlags(final int keyLabelFlags) {
|
||||||
|
mDefaultKeyLabelFlags = keyLabelFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDefaultBackgroundType() {
|
||||||
|
return mDefaultBackgroundType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultBackgroundType(final int backgroundType) {
|
||||||
|
mDefaultBackgroundType = backgroundType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setXPos(final float keyXPos) {
|
||||||
|
mCurrentX = keyXPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void advanceXPos(final float width) {
|
||||||
|
mCurrentX += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeyY() {
|
||||||
|
return mCurrentY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getKeyX(final TypedArray keyAttr) {
|
||||||
|
final int keyboardRightEdge = mParams.mOccupiedWidth
|
||||||
|
- mParams.mHorizontalEdgesPadding;
|
||||||
|
if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
|
||||||
|
final float keyXPos = ResourceUtils.getDimensionOrFraction(keyAttr,
|
||||||
|
R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0);
|
||||||
|
if (keyXPos < 0) {
|
||||||
|
// If keyXPos is negative, the actual x-coordinate will be
|
||||||
|
// keyboardWidth + keyXPos.
|
||||||
|
// keyXPos shouldn't be less than mCurrentX because drawable area for this
|
||||||
|
// key starts at mCurrentX. Or, this key will overlaps the adjacent key on
|
||||||
|
// its left hand side.
|
||||||
|
return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
|
||||||
|
} else {
|
||||||
|
return keyXPos + mParams.mHorizontalEdgesPadding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mCurrentX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getKeyWidth(final TypedArray keyAttr) {
|
||||||
|
return getKeyWidth(keyAttr, mCurrentX);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getKeyWidth(final TypedArray keyAttr, final float keyXPos) {
|
||||||
|
final int widthType = ResourceUtils.getEnumValue(keyAttr,
|
||||||
|
R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
|
||||||
|
switch (widthType) {
|
||||||
|
case KEYWIDTH_FILL_RIGHT:
|
||||||
|
final int keyboardRightEdge =
|
||||||
|
mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
|
||||||
|
// If keyWidth is fillRight, the actual key width will be determined to fill
|
||||||
|
// out the area up to the right edge of the keyboard.
|
||||||
|
return keyboardRightEdge - keyXPos;
|
||||||
|
default: // KEYWIDTH_NOT_ENUM
|
||||||
|
return ResourceUtils.getDimensionOrFraction(keyAttr,
|
||||||
|
R.styleable.Keyboard_Key_keyWidth,
|
||||||
|
mParams.mBaseWidth, mDefaultKeyWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.inputmethod.keyboard.internal;
|
||||||
|
|
||||||
|
import com.android.inputmethod.keyboard.Key;
|
||||||
|
import com.android.inputmethod.latin.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class KeysCache {
|
||||||
|
private final HashMap<Key, Key> mMap = CollectionUtils.newHashMap();
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
mMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key get(final Key key) {
|
||||||
|
final Key existingKey = mMap.get(key);
|
||||||
|
if (existingKey != null) {
|
||||||
|
// Reuse the existing element that equals to "key" without adding "key" to the map.
|
||||||
|
return existingKey;
|
||||||
|
}
|
||||||
|
mMap.put(key, key);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.inputmethod.keyboard.internal;
|
||||||
|
|
||||||
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
|
import com.android.inputmethod.latin.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class MoreKeySpec {
|
||||||
|
public final int mCode;
|
||||||
|
public final String mLabel;
|
||||||
|
public final String mOutputText;
|
||||||
|
public final int mIconId;
|
||||||
|
|
||||||
|
public MoreKeySpec(final String moreKeySpec, boolean needsToUpperCase, final Locale locale,
|
||||||
|
final KeyboardCodesSet codesSet) {
|
||||||
|
mLabel = KeySpecParser.toUpperCaseOfStringForLocale(
|
||||||
|
KeySpecParser.getLabel(moreKeySpec), needsToUpperCase, locale);
|
||||||
|
final int code = KeySpecParser.toUpperCaseOfCodeForLocale(
|
||||||
|
KeySpecParser.getCode(moreKeySpec, codesSet), needsToUpperCase, locale);
|
||||||
|
if (code == Keyboard.CODE_UNSPECIFIED) {
|
||||||
|
// Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters
|
||||||
|
// upper case representation ("SS").
|
||||||
|
mCode = Keyboard.CODE_OUTPUT_TEXT;
|
||||||
|
mOutputText = mLabel;
|
||||||
|
} else {
|
||||||
|
mCode = code;
|
||||||
|
mOutputText = KeySpecParser.toUpperCaseOfStringForLocale(
|
||||||
|
KeySpecParser.getOutputText(moreKeySpec), needsToUpperCase, locale);
|
||||||
|
}
|
||||||
|
mIconId = KeySpecParser.getIconId(moreKeySpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel
|
||||||
|
: KeySpecParser.PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId));
|
||||||
|
final String output = (mCode == Keyboard.CODE_OUTPUT_TEXT ? mOutputText
|
||||||
|
: Keyboard.printableCode(mCode));
|
||||||
|
if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) {
|
||||||
|
return output;
|
||||||
|
} else {
|
||||||
|
return label + "|" + output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.inputmethod.keyboard.internal;
|
||||||
|
|
||||||
|
import com.android.inputmethod.latin.LatinImeLogger;
|
||||||
|
|
||||||
|
public class TouchPositionCorrection {
|
||||||
|
private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3;
|
||||||
|
|
||||||
|
public boolean mEnabled;
|
||||||
|
public float[] mXs;
|
||||||
|
public float[] mYs;
|
||||||
|
public float[] mRadii;
|
||||||
|
|
||||||
|
public void load(final String[] data) {
|
||||||
|
final int dataLength = data.length;
|
||||||
|
if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
|
||||||
|
if (LatinImeLogger.sDBG) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"the size of touch position correction data is invalid");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
|
||||||
|
mXs = new float[length];
|
||||||
|
mYs = new float[length];
|
||||||
|
mRadii = new float[length];
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < dataLength; ++i) {
|
||||||
|
final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE;
|
||||||
|
final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
|
||||||
|
final float value = Float.parseFloat(data[i]);
|
||||||
|
if (type == 0) {
|
||||||
|
mXs[index] = value;
|
||||||
|
} else if (type == 1) {
|
||||||
|
mYs[index] = value;
|
||||||
|
} else {
|
||||||
|
mRadii[index] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
if (LatinImeLogger.sDBG) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"the number format for touch position correction data is invalid");
|
||||||
|
}
|
||||||
|
mXs = null;
|
||||||
|
mYs = null;
|
||||||
|
mRadii = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this method.
|
||||||
|
public void setEnabled(final boolean enabled) {
|
||||||
|
mEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValid() {
|
||||||
|
return mEnabled && mXs != null && mYs != null && mRadii != null
|
||||||
|
&& mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,9 @@ 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.Keyboard;
|
||||||
import com.android.inputmethod.keyboard.KeyboardSwitcher;
|
import com.android.inputmethod.keyboard.KeyboardSwitcher;
|
||||||
|
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
|
||||||
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.latin.R;
|
import com.android.inputmethod.latin.R;
|
||||||
import com.android.inputmethod.latin.SuggestedWords;
|
import com.android.inputmethod.latin.SuggestedWords;
|
||||||
import com.android.inputmethod.latin.Utils;
|
import com.android.inputmethod.latin.Utils;
|
||||||
|
@ -31,145 +33,149 @@ import com.android.inputmethod.latin.Utils;
|
||||||
public class MoreSuggestions extends Keyboard {
|
public class MoreSuggestions extends Keyboard {
|
||||||
public static final int SUGGESTION_CODE_BASE = 1024;
|
public static final int SUGGESTION_CODE_BASE = 1024;
|
||||||
|
|
||||||
MoreSuggestions(Builder.MoreSuggestionsParam params) {
|
MoreSuggestions(final MoreSuggestionsParam params) {
|
||||||
super(params);
|
super(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder extends Keyboard.Builder<Builder.MoreSuggestionsParam> {
|
private static class MoreSuggestionsParam extends KeyboardParams {
|
||||||
|
private final int[] mWidths = new int[SuggestionStripView.MAX_SUGGESTIONS];
|
||||||
|
private final int[] mRowNumbers = new int[SuggestionStripView.MAX_SUGGESTIONS];
|
||||||
|
private final int[] mColumnOrders = new int[SuggestionStripView.MAX_SUGGESTIONS];
|
||||||
|
private final int[] mNumColumnsInRow = new int[SuggestionStripView.MAX_SUGGESTIONS];
|
||||||
|
private static final int MAX_COLUMNS_IN_ROW = 3;
|
||||||
|
private int mNumRows;
|
||||||
|
public Drawable mDivider;
|
||||||
|
public int mDividerWidth;
|
||||||
|
|
||||||
|
public MoreSuggestionsParam() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int layout(final SuggestedWords suggestions, final int fromPos, final int maxWidth,
|
||||||
|
final int minWidth, final int maxRow, final MoreSuggestionsView view) {
|
||||||
|
clearKeys();
|
||||||
|
final Resources res = view.getContext().getResources();
|
||||||
|
mDivider = res.getDrawable(R.drawable.more_suggestions_divider);
|
||||||
|
mDividerWidth = mDivider.getIntrinsicWidth();
|
||||||
|
final int padding = (int) res.getDimension(
|
||||||
|
R.dimen.more_suggestions_key_horizontal_padding);
|
||||||
|
final Paint paint = view.newDefaultLabelPaint();
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
int pos = fromPos, rowStartPos = fromPos;
|
||||||
|
final int size = Math.min(suggestions.size(), SuggestionStripView.MAX_SUGGESTIONS);
|
||||||
|
while (pos < size) {
|
||||||
|
final String word = suggestions.getWord(pos).toString();
|
||||||
|
// TODO: Should take care of text x-scaling.
|
||||||
|
mWidths[pos] = (int)view.getLabelWidth(word, paint) + padding;
|
||||||
|
final int numColumn = pos - rowStartPos + 1;
|
||||||
|
final int columnWidth =
|
||||||
|
(maxWidth - mDividerWidth * (numColumn - 1)) / numColumn;
|
||||||
|
if (numColumn > MAX_COLUMNS_IN_ROW
|
||||||
|
|| !fitInWidth(rowStartPos, pos + 1, columnWidth)) {
|
||||||
|
if ((row + 1) >= maxRow) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mNumColumnsInRow[row] = pos - rowStartPos;
|
||||||
|
rowStartPos = pos;
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
mColumnOrders[pos] = pos - rowStartPos;
|
||||||
|
mRowNumbers[pos] = row;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
mNumColumnsInRow[row] = pos - rowStartPos;
|
||||||
|
mNumRows = row + 1;
|
||||||
|
mBaseWidth = mOccupiedWidth = Math.max(
|
||||||
|
minWidth, calcurateMaxRowWidth(fromPos, pos));
|
||||||
|
mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
|
||||||
|
return pos - fromPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean fitInWidth(final int startPos, final int endPos, final int width) {
|
||||||
|
for (int pos = startPos; pos < endPos; pos++) {
|
||||||
|
if (mWidths[pos] > width)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calcurateMaxRowWidth(final int startPos, final int endPos) {
|
||||||
|
int maxRowWidth = 0;
|
||||||
|
int pos = startPos;
|
||||||
|
for (int row = 0; row < mNumRows; row++) {
|
||||||
|
final int numColumnInRow = mNumColumnsInRow[row];
|
||||||
|
int maxKeyWidth = 0;
|
||||||
|
while (pos < endPos && mRowNumbers[pos] == row) {
|
||||||
|
maxKeyWidth = Math.max(maxKeyWidth, mWidths[pos]);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
maxRowWidth = Math.max(maxRowWidth,
|
||||||
|
maxKeyWidth * numColumnInRow + mDividerWidth * (numColumnInRow - 1));
|
||||||
|
}
|
||||||
|
return maxRowWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int[][] COLUMN_ORDER_TO_NUMBER = {
|
||||||
|
{ 0, },
|
||||||
|
{ 1, 0, },
|
||||||
|
{ 2, 0, 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
public int getNumColumnInRow(final int pos) {
|
||||||
|
return mNumColumnsInRow[mRowNumbers[pos]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColumnNumber(final int pos) {
|
||||||
|
final int columnOrder = mColumnOrders[pos];
|
||||||
|
final int numColumn = getNumColumnInRow(pos);
|
||||||
|
return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX(final int pos) {
|
||||||
|
final int columnNumber = getColumnNumber(pos);
|
||||||
|
return columnNumber * (getWidth(pos) + mDividerWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY(final int pos) {
|
||||||
|
final int row = mRowNumbers[pos];
|
||||||
|
return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth(final int pos) {
|
||||||
|
final int numColumnInRow = getNumColumnInRow(pos);
|
||||||
|
return (mOccupiedWidth - mDividerWidth * (numColumnInRow - 1)) / numColumnInRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markAsEdgeKey(final Key key, final int pos) {
|
||||||
|
final int row = mRowNumbers[pos];
|
||||||
|
if (row == 0)
|
||||||
|
key.markAsBottomEdge(this);
|
||||||
|
if (row == mNumRows - 1)
|
||||||
|
key.markAsTopEdge(this);
|
||||||
|
|
||||||
|
final int numColumnInRow = mNumColumnsInRow[row];
|
||||||
|
final int column = getColumnNumber(pos);
|
||||||
|
if (column == 0)
|
||||||
|
key.markAsLeftEdge(this);
|
||||||
|
if (column == numColumnInRow - 1)
|
||||||
|
key.markAsRightEdge(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder extends KeyboardBuilder<MoreSuggestionsParam> {
|
||||||
private final MoreSuggestionsView mPaneView;
|
private final MoreSuggestionsView mPaneView;
|
||||||
private SuggestedWords mSuggestions;
|
private SuggestedWords mSuggestions;
|
||||||
private int mFromPos;
|
private int mFromPos;
|
||||||
private int mToPos;
|
private int mToPos;
|
||||||
|
|
||||||
public static class MoreSuggestionsParam extends Keyboard.Params {
|
public Builder(final MoreSuggestionsView paneView) {
|
||||||
private final int[] mWidths = new int[SuggestionStripView.MAX_SUGGESTIONS];
|
|
||||||
private final int[] mRowNumbers = new int[SuggestionStripView.MAX_SUGGESTIONS];
|
|
||||||
private final int[] mColumnOrders = new int[SuggestionStripView.MAX_SUGGESTIONS];
|
|
||||||
private final int[] mNumColumnsInRow = new int[SuggestionStripView.MAX_SUGGESTIONS];
|
|
||||||
private static final int MAX_COLUMNS_IN_ROW = 3;
|
|
||||||
private int mNumRows;
|
|
||||||
public Drawable mDivider;
|
|
||||||
public int mDividerWidth;
|
|
||||||
|
|
||||||
public int layout(SuggestedWords suggestions, int fromPos, int maxWidth, int minWidth,
|
|
||||||
int maxRow, MoreSuggestionsView view) {
|
|
||||||
clearKeys();
|
|
||||||
final Resources res = view.getContext().getResources();
|
|
||||||
mDivider = res.getDrawable(R.drawable.more_suggestions_divider);
|
|
||||||
mDividerWidth = mDivider.getIntrinsicWidth();
|
|
||||||
final int padding = (int) res.getDimension(
|
|
||||||
R.dimen.more_suggestions_key_horizontal_padding);
|
|
||||||
final Paint paint = view.newDefaultLabelPaint();
|
|
||||||
|
|
||||||
int row = 0;
|
|
||||||
int pos = fromPos, rowStartPos = fromPos;
|
|
||||||
final int size = Math.min(suggestions.size(), SuggestionStripView.MAX_SUGGESTIONS);
|
|
||||||
while (pos < size) {
|
|
||||||
final String word = suggestions.getWord(pos).toString();
|
|
||||||
// TODO: Should take care of text x-scaling.
|
|
||||||
mWidths[pos] = (int)view.getLabelWidth(word, paint) + padding;
|
|
||||||
final int numColumn = pos - rowStartPos + 1;
|
|
||||||
final int columnWidth =
|
|
||||||
(maxWidth - mDividerWidth * (numColumn - 1)) / numColumn;
|
|
||||||
if (numColumn > MAX_COLUMNS_IN_ROW
|
|
||||||
|| !fitInWidth(rowStartPos, pos + 1, columnWidth)) {
|
|
||||||
if ((row + 1) >= maxRow) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mNumColumnsInRow[row] = pos - rowStartPos;
|
|
||||||
rowStartPos = pos;
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
mColumnOrders[pos] = pos - rowStartPos;
|
|
||||||
mRowNumbers[pos] = row;
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
mNumColumnsInRow[row] = pos - rowStartPos;
|
|
||||||
mNumRows = row + 1;
|
|
||||||
mBaseWidth = mOccupiedWidth = Math.max(
|
|
||||||
minWidth, calcurateMaxRowWidth(fromPos, pos));
|
|
||||||
mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
|
|
||||||
return pos - fromPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean fitInWidth(int startPos, int endPos, int width) {
|
|
||||||
for (int pos = startPos; pos < endPos; pos++) {
|
|
||||||
if (mWidths[pos] > width)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int calcurateMaxRowWidth(int startPos, int endPos) {
|
|
||||||
int maxRowWidth = 0;
|
|
||||||
int pos = startPos;
|
|
||||||
for (int row = 0; row < mNumRows; row++) {
|
|
||||||
final int numColumnInRow = mNumColumnsInRow[row];
|
|
||||||
int maxKeyWidth = 0;
|
|
||||||
while (pos < endPos && mRowNumbers[pos] == row) {
|
|
||||||
maxKeyWidth = Math.max(maxKeyWidth, mWidths[pos]);
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
maxRowWidth = Math.max(maxRowWidth,
|
|
||||||
maxKeyWidth * numColumnInRow + mDividerWidth * (numColumnInRow - 1));
|
|
||||||
}
|
|
||||||
return maxRowWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int[][] COLUMN_ORDER_TO_NUMBER = {
|
|
||||||
{ 0, },
|
|
||||||
{ 1, 0, },
|
|
||||||
{ 2, 0, 1},
|
|
||||||
};
|
|
||||||
|
|
||||||
public int getNumColumnInRow(int pos) {
|
|
||||||
return mNumColumnsInRow[mRowNumbers[pos]];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getColumnNumber(int pos) {
|
|
||||||
final int columnOrder = mColumnOrders[pos];
|
|
||||||
final int numColumn = getNumColumnInRow(pos);
|
|
||||||
return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getX(int pos) {
|
|
||||||
final int columnNumber = getColumnNumber(pos);
|
|
||||||
return columnNumber * (getWidth(pos) + mDividerWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getY(int pos) {
|
|
||||||
final int row = mRowNumbers[pos];
|
|
||||||
return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getWidth(int pos) {
|
|
||||||
final int numColumnInRow = getNumColumnInRow(pos);
|
|
||||||
return (mOccupiedWidth - mDividerWidth * (numColumnInRow - 1)) / numColumnInRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void markAsEdgeKey(Key key, int pos) {
|
|
||||||
final int row = mRowNumbers[pos];
|
|
||||||
if (row == 0)
|
|
||||||
key.markAsBottomEdge(this);
|
|
||||||
if (row == mNumRows - 1)
|
|
||||||
key.markAsTopEdge(this);
|
|
||||||
|
|
||||||
final int numColumnInRow = mNumColumnsInRow[row];
|
|
||||||
final int column = getColumnNumber(pos);
|
|
||||||
if (column == 0)
|
|
||||||
key.markAsLeftEdge(this);
|
|
||||||
if (column == numColumnInRow - 1)
|
|
||||||
key.markAsRightEdge(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder(MoreSuggestionsView paneView) {
|
|
||||||
super(paneView.getContext(), new MoreSuggestionsParam());
|
super(paneView.getContext(), new MoreSuggestionsParam());
|
||||||
mPaneView = paneView;
|
mPaneView = paneView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder layout(SuggestedWords suggestions, int fromPos, int maxWidth,
|
public Builder layout(final SuggestedWords suggestions, final int fromPos,
|
||||||
int minWidth, int maxRow) {
|
final int maxWidth, final int minWidth, final int maxRow) {
|
||||||
final Keyboard keyboard = KeyboardSwitcher.getInstance().getKeyboard();
|
final Keyboard keyboard = KeyboardSwitcher.getInstance().getKeyboard();
|
||||||
final int xmlId = R.xml.kbd_suggestions_pane_template;
|
final int xmlId = R.xml.kbd_suggestions_pane_template;
|
||||||
load(xmlId, keyboard.mId);
|
load(xmlId, keyboard.mId);
|
||||||
|
@ -183,25 +189,6 @@ public class MoreSuggestions extends Keyboard {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Divider extends Key.Spacer {
|
|
||||||
private final Drawable mIcon;
|
|
||||||
|
|
||||||
public Divider(Keyboard.Params params, Drawable icon, int x, int y, int width,
|
|
||||||
int height) {
|
|
||||||
super(params, x, y, width, height);
|
|
||||||
mIcon = icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Drawable getIcon(KeyboardIconsSet iconSet, int alpha) {
|
|
||||||
// KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the
|
|
||||||
// constructor.
|
|
||||||
// TODO: Drawable itself should have an alpha value.
|
|
||||||
mIcon.setAlpha(128);
|
|
||||||
return mIcon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MoreSuggestions build() {
|
public MoreSuggestions build() {
|
||||||
final MoreSuggestionsParam params = mParams;
|
final MoreSuggestionsParam params = mParams;
|
||||||
|
@ -228,4 +215,23 @@ public class MoreSuggestions extends Keyboard {
|
||||||
return new MoreSuggestions(params);
|
return new MoreSuggestions(params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class Divider extends Key.Spacer {
|
||||||
|
private final Drawable mIcon;
|
||||||
|
|
||||||
|
public Divider(final KeyboardParams params, final Drawable icon, final int x,
|
||||||
|
final int y, final int width, final int height) {
|
||||||
|
super(params, x, y, width, height);
|
||||||
|
mIcon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) {
|
||||||
|
// KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the
|
||||||
|
// constructor.
|
||||||
|
// TODO: Drawable itself should have an alpha value.
|
||||||
|
mIcon.setAlpha(128);
|
||||||
|
return mIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard;
|
||||||
|
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.MoreKeysKeyboard.Builder.MoreKeysKeyboardParams;
|
import com.android.inputmethod.keyboard.MoreKeysKeyboard.MoreKeysKeyboardParams;
|
||||||
|
|
||||||
public class MoreKeysKeyboardBuilderFixedOrderTests extends AndroidTestCase {
|
public class MoreKeysKeyboardBuilderFixedOrderTests extends AndroidTestCase {
|
||||||
private static final int WIDTH = 10;
|
private static final int WIDTH = 10;
|
||||||
|
|
|
@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard;
|
||||||
|
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.MoreKeysKeyboard.Builder.MoreKeysKeyboardParams;
|
import com.android.inputmethod.keyboard.MoreKeysKeyboard.MoreKeysKeyboardParams;
|
||||||
|
|
||||||
public class MoreKeysKeyboardBuilderTests extends AndroidTestCase {
|
public class MoreKeysKeyboardBuilderTests extends AndroidTestCase {
|
||||||
private static final int WIDTH = 10;
|
private static final int WIDTH = 10;
|
||||||
|
|
|
@ -23,7 +23,6 @@ import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UN
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.Keyboard;
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
import com.android.inputmethod.keyboard.internal.KeySpecParser.MoreKeySpec;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
Loading…
Reference in New Issue