Refactoring keyboard drawing code and KeyDetector
Change-Id: I55009bf38b1422301223bd90463f837562559dc5main
parent
ffca763050
commit
dc90d0a15f
|
@ -16,35 +16,35 @@
|
|||
|
||||
package com.android.inputmethod.keyboard;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class KeyDetector {
|
||||
public static final int NOT_A_KEY = -1;
|
||||
public class KeyDetector {
|
||||
private static final String TAG = KeyDetector.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
public static final int NOT_A_CODE = -1;
|
||||
public static final int NOT_A_KEY = -1;
|
||||
|
||||
protected Keyboard mKeyboard;
|
||||
private Keyboard mKeyboard;
|
||||
private int mCorrectionX;
|
||||
private int mCorrectionY;
|
||||
private boolean mProximityCorrectOn;
|
||||
private int mProximityThresholdSquare;
|
||||
|
||||
private Key[] mKeys;
|
||||
// working area
|
||||
private static final int MAX_NEARBY_KEYS = 12;
|
||||
private final int[] mDistances = new int[MAX_NEARBY_KEYS];
|
||||
private final int[] mIndices = new int[MAX_NEARBY_KEYS];
|
||||
|
||||
protected int mCorrectionX;
|
||||
|
||||
protected int mCorrectionY;
|
||||
|
||||
protected boolean mProximityCorrectOn;
|
||||
|
||||
protected int mProximityThresholdSquare;
|
||||
|
||||
public Key[] setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
|
||||
public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
|
||||
if (keyboard == null)
|
||||
throw new NullPointerException();
|
||||
mCorrectionX = (int)correctionX;
|
||||
mCorrectionY = (int)correctionY;
|
||||
mKeyboard = keyboard;
|
||||
List<Key> keys = mKeyboard.getKeys();
|
||||
Key[] array = keys.toArray(new Key[keys.size()]);
|
||||
mKeys = array;
|
||||
return array;
|
||||
}
|
||||
|
||||
protected int getTouchX(int x) {
|
||||
|
@ -55,11 +55,11 @@ public abstract class KeyDetector {
|
|||
return y + mCorrectionY;
|
||||
}
|
||||
|
||||
protected Key[] getKeys() {
|
||||
if (mKeys == null)
|
||||
protected List<Key> getKeys() {
|
||||
if (mKeyboard == null)
|
||||
throw new IllegalStateException("keyboard isn't set");
|
||||
// mKeyboard is guaranteed not to be null at setKeybaord() method if mKeys is not null
|
||||
return mKeys;
|
||||
return mKeyboard.getKeys();
|
||||
}
|
||||
|
||||
public void setProximityCorrectionEnabled(boolean enabled) {
|
||||
|
@ -74,6 +74,17 @@ public abstract class KeyDetector {
|
|||
mProximityThresholdSquare = threshold * threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes maximum size of the array that can contain all nearby key indices returned by
|
||||
* {@link #getKeyIndexAndNearbyCodes}.
|
||||
*
|
||||
* @return Returns maximum size of the array that can contain all nearby key indices returned
|
||||
* by {@link #getKeyIndexAndNearbyCodes}.
|
||||
*/
|
||||
protected int getMaxNearbyKeys() {
|
||||
return MAX_NEARBY_KEYS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates array that can hold all key indices returned by {@link #getKeyIndexAndNearbyCodes}
|
||||
* method. The maximum size of the array should be computed by {@link #getMaxNearbyKeys}.
|
||||
|
@ -88,14 +99,60 @@ public abstract class KeyDetector {
|
|||
return codes;
|
||||
}
|
||||
|
||||
private void initializeNearbyKeys() {
|
||||
Arrays.fill(mDistances, Integer.MAX_VALUE);
|
||||
Arrays.fill(mIndices, NOT_A_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes maximum size of the array that can contain all nearby key indices returned by
|
||||
* {@link #getKeyIndexAndNearbyCodes}.
|
||||
* Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
|
||||
*
|
||||
* @return Returns maximum size of the array that can contain all nearby key indices returned
|
||||
* by {@link #getKeyIndexAndNearbyCodes}.
|
||||
* @param keyIndex index of the key.
|
||||
* @param distance distance between the key's edge and user touched point.
|
||||
* @return order of the key in the nearby buffer, 0 if it is the nearest key.
|
||||
*/
|
||||
abstract protected int getMaxNearbyKeys();
|
||||
private int sortNearbyKeys(int keyIndex, int distance) {
|
||||
final int[] distances = mDistances;
|
||||
final int[] indices = mIndices;
|
||||
for (int insertPos = 0; insertPos < distances.length; insertPos++) {
|
||||
if (distance < distances[insertPos]) {
|
||||
final int nextPos = insertPos + 1;
|
||||
if (nextPos < distances.length) {
|
||||
System.arraycopy(distances, insertPos, distances, nextPos,
|
||||
distances.length - nextPos);
|
||||
System.arraycopy(indices, insertPos, indices, nextPos,
|
||||
indices.length - nextPos);
|
||||
}
|
||||
distances[insertPos] = distance;
|
||||
indices[insertPos] = keyIndex;
|
||||
return insertPos;
|
||||
}
|
||||
}
|
||||
return distances.length;
|
||||
}
|
||||
|
||||
private void getNearbyKeyCodes(final int[] allCodes) {
|
||||
final List<Key> keys = getKeys();
|
||||
final int[] indices = mIndices;
|
||||
|
||||
// allCodes[0] should always have the key code even if it is a non-letter key.
|
||||
if (indices[0] == NOT_A_KEY) {
|
||||
allCodes[0] = NOT_A_CODE;
|
||||
return;
|
||||
}
|
||||
|
||||
int numCodes = 0;
|
||||
for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) {
|
||||
final int index = indices[j];
|
||||
if (index == NOT_A_KEY)
|
||||
break;
|
||||
final int code = keys.get(index).mCode;
|
||||
// filter out a non-letter key from nearby keys
|
||||
if (code < Keyboard.CODE_SPACE)
|
||||
continue;
|
||||
allCodes[numCodes++] = code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all possible nearby key indices around a touch event point and returns the nearest key
|
||||
|
@ -108,5 +165,34 @@ public abstract class KeyDetector {
|
|||
* @param allCodes All nearby key code except functional key are returned in this array
|
||||
* @return The nearest key index
|
||||
*/
|
||||
abstract public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes);
|
||||
public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
|
||||
final List<Key> keys = getKeys();
|
||||
final int touchX = getTouchX(x);
|
||||
final int touchY = getTouchY(y);
|
||||
|
||||
initializeNearbyKeys();
|
||||
int primaryIndex = NOT_A_KEY;
|
||||
for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) {
|
||||
final Key key = keys.get(index);
|
||||
final boolean isInside = key.isInside(touchX, touchY);
|
||||
final int distance = key.squaredDistanceToEdge(touchX, touchY);
|
||||
if (isInside || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
|
||||
final int insertedPosition = sortNearbyKeys(index, distance);
|
||||
if (insertedPosition == 0 && isInside)
|
||||
primaryIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
if (allCodes != null && allCodes.length > 0) {
|
||||
getNearbyKeyCodes(allCodes);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "x=" + x + " y=" + y
|
||||
+ " primary="
|
||||
+ (primaryIndex == NOT_A_KEY ? "none" : keys.get(primaryIndex).mCode)
|
||||
+ " codes=" + Arrays.toString(allCodes));
|
||||
}
|
||||
}
|
||||
|
||||
return primaryIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,7 +106,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
|||
|
||||
// Main keyboard
|
||||
private Keyboard mKeyboard;
|
||||
private Key[] mKeys;
|
||||
|
||||
// Key preview popup
|
||||
private boolean mInForeground;
|
||||
|
@ -147,7 +146,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
|||
// Accessibility
|
||||
private boolean mIsAccessibilityEnabled;
|
||||
|
||||
protected KeyDetector mKeyDetector = new ProximityKeyDetector();
|
||||
protected KeyDetector mKeyDetector = new KeyDetector();
|
||||
|
||||
// Swipe gesture detector
|
||||
private GestureDetector mGestureDetector;
|
||||
|
@ -493,10 +492,10 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
|||
mHandler.cancelPopupPreview();
|
||||
mKeyboard = keyboard;
|
||||
LatinImeLogger.onSetKeyboard(keyboard);
|
||||
mKeys = mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
|
||||
mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
|
||||
-getPaddingTop() + mVerticalCorrection);
|
||||
for (PointerTracker tracker : mPointerTrackers) {
|
||||
tracker.setKeyboard(keyboard, mKeys, mKeyHysteresisDistance);
|
||||
tracker.setKeyboard(keyboard, mKeyHysteresisDistance);
|
||||
}
|
||||
requestLayout();
|
||||
mKeyboardChanged = true;
|
||||
|
@ -638,154 +637,32 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
|||
}
|
||||
final Canvas canvas = mCanvas;
|
||||
canvas.clipRect(mDirtyRect, Op.REPLACE);
|
||||
canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
|
||||
|
||||
if (mKeyboard == null) return;
|
||||
|
||||
final Paint paint = mPaint;
|
||||
final Drawable keyBackground = mKeyBackground;
|
||||
final Rect padding = mPadding;
|
||||
final int kbdPaddingLeft = getPaddingLeft();
|
||||
final int kbdPaddingTop = getPaddingTop();
|
||||
final Key[] keys = mKeys;
|
||||
final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase();
|
||||
final boolean drawSingleKey = (mInvalidatedKey != null
|
||||
&& mInvalidatedKeyRect.contains(mDirtyRect));
|
||||
|
||||
canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
|
||||
final int keyCount = keys.length;
|
||||
for (int i = 0; i < keyCount; i++) {
|
||||
final Key key = keys[i];
|
||||
if (drawSingleKey && key != mInvalidatedKey) {
|
||||
continue;
|
||||
if (mInvalidatedKey != null && mInvalidatedKeyRect.contains(mDirtyRect)) {
|
||||
// Draw a single key.
|
||||
onBufferDrawKey(canvas, mInvalidatedKey);
|
||||
} else {
|
||||
// Draw all keys.
|
||||
for (final Key key : mKeyboard.getKeys()) {
|
||||
onBufferDrawKey(canvas, key);
|
||||
}
|
||||
int[] drawableState = key.getCurrentDrawableState();
|
||||
keyBackground.setState(drawableState);
|
||||
|
||||
// Switch the character to uppercase if shift is pressed
|
||||
String label = key.mLabel == null? null : adjustCase(key.mLabel).toString();
|
||||
|
||||
final int keyDrawX = key.mX + key.mVisualInsetsLeft;
|
||||
final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
|
||||
final Rect bounds = keyBackground.getBounds();
|
||||
if (keyDrawWidth != bounds.right || key.mHeight != bounds.bottom) {
|
||||
keyBackground.setBounds(0, 0, keyDrawWidth, key.mHeight);
|
||||
}
|
||||
canvas.translate(keyDrawX + kbdPaddingLeft, key.mY + kbdPaddingTop);
|
||||
keyBackground.draw(canvas);
|
||||
|
||||
final int rowHeight = padding.top + key.mHeight;
|
||||
// Draw key label
|
||||
if (label != null) {
|
||||
// For characters, use large font. For labels like "Done", use small font.
|
||||
final int labelSize = getLabelSizeAndSetPaint(label, key.mLabelOption, paint);
|
||||
final int labelCharHeight = getLabelCharHeight(labelSize, paint);
|
||||
|
||||
// Vertical label text alignment.
|
||||
final float baseline;
|
||||
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_BOTTOM) != 0) {
|
||||
baseline = key.mHeight -
|
||||
+ labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR;
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
|
||||
new Paint());
|
||||
} else { // Align center
|
||||
final float centerY = (key.mHeight + padding.top - padding.bottom) / 2;
|
||||
baseline = centerY
|
||||
+ labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER;
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
|
||||
new Paint());
|
||||
}
|
||||
// Horizontal label text alignment
|
||||
final int positionX;
|
||||
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
|
||||
positionX = mKeyLabelHorizontalPadding + padding.left;
|
||||
paint.setTextAlign(Align.LEFT);
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, new Paint());
|
||||
} else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
|
||||
positionX = keyDrawWidth - mKeyLabelHorizontalPadding - padding.right;
|
||||
paint.setTextAlign(Align.RIGHT);
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawVerticalLine(canvas, positionX, rowHeight, 0xc0808000, new Paint());
|
||||
} else {
|
||||
positionX = (keyDrawWidth + padding.left - padding.right) / 2;
|
||||
paint.setTextAlign(Align.CENTER);
|
||||
if (DEBUG_SHOW_ALIGN) {
|
||||
if (label.length() > 1)
|
||||
drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint());
|
||||
}
|
||||
}
|
||||
if (key.mManualTemporaryUpperCaseHintIcon != null && isManualTemporaryUpperCase) {
|
||||
paint.setColor(mKeyTextColorDisabled);
|
||||
} else {
|
||||
paint.setColor(mKeyTextColor);
|
||||
}
|
||||
if (key.mEnabled) {
|
||||
// Set a drop shadow for the text
|
||||
paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
|
||||
} else {
|
||||
// Make label invisible
|
||||
paint.setColor(Color.TRANSPARENT);
|
||||
}
|
||||
canvas.drawText(label, positionX, baseline, paint);
|
||||
// Turn off drop shadow
|
||||
paint.setShadowLayer(0, 0, 0, 0);
|
||||
}
|
||||
// Draw key icon
|
||||
final Drawable icon = key.getIcon();
|
||||
if (key.mLabel == null && icon != null) {
|
||||
final int drawableWidth = icon.getIntrinsicWidth();
|
||||
final int drawableHeight = icon.getIntrinsicHeight();
|
||||
final int drawableX;
|
||||
final int drawableY = (
|
||||
key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
|
||||
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
|
||||
drawableX = padding.left + mKeyLabelHorizontalPadding;
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawVerticalLine(canvas, drawableX, rowHeight, 0xc0800080, new Paint());
|
||||
} else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
|
||||
drawableX = keyDrawWidth - padding.right - mKeyLabelHorizontalPadding
|
||||
- drawableWidth;
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawVerticalLine(canvas, drawableX + drawableWidth, rowHeight,
|
||||
0xc0808000, new Paint());
|
||||
} else { // Align center
|
||||
drawableX = (keyDrawWidth + padding.left - padding.right - drawableWidth) / 2;
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight,
|
||||
0xc0008080, new Paint());
|
||||
}
|
||||
drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight);
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
|
||||
0x80c00000, new Paint());
|
||||
}
|
||||
if (key.mHintIcon != null) {
|
||||
final int drawableWidth = keyDrawWidth;
|
||||
final int drawableHeight = key.mHeight;
|
||||
final int drawableX = 0;
|
||||
final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL;
|
||||
Drawable hintIcon = (isManualTemporaryUpperCase
|
||||
&& key.mManualTemporaryUpperCaseHintIcon != null)
|
||||
? key.mManualTemporaryUpperCaseHintIcon : key.mHintIcon;
|
||||
drawIcon(canvas, hintIcon, drawableX, drawableY, drawableWidth, drawableHeight);
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
|
||||
0x80c0c000, new Paint());
|
||||
}
|
||||
canvas.translate(-keyDrawX - kbdPaddingLeft, -key.mY - kbdPaddingTop);
|
||||
}
|
||||
|
||||
// TODO: Move this function to ProximityInfo for getting rid of public declarations for
|
||||
// TODO: Move this function to ProximityInfo for getting rid of
|
||||
// public declarations for
|
||||
// GRID_WIDTH and GRID_HEIGHT
|
||||
if (DEBUG_KEYBOARD_GRID) {
|
||||
Paint p = new Paint();
|
||||
p.setStyle(Paint.Style.STROKE);
|
||||
p.setStrokeWidth(1.0f);
|
||||
p.setColor(0x800000c0);
|
||||
int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1) / mKeyboard.GRID_WIDTH;
|
||||
int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1) / mKeyboard.GRID_HEIGHT;
|
||||
int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1)
|
||||
/ mKeyboard.GRID_WIDTH;
|
||||
int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1)
|
||||
/ mKeyboard.GRID_HEIGHT;
|
||||
for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++)
|
||||
canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p);
|
||||
for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++)
|
||||
|
@ -794,8 +671,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
|||
|
||||
// Overlay a dark rectangle to dim the keyboard
|
||||
if (mMiniKeyboardView != null) {
|
||||
paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
|
||||
canvas.drawRect(0, 0, width, height, paint);
|
||||
mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
|
||||
canvas.drawRect(0, 0, width, height, mPaint);
|
||||
}
|
||||
|
||||
mInvalidatedKey = null;
|
||||
|
@ -803,6 +680,134 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
|||
mDirtyRect.setEmpty();
|
||||
}
|
||||
|
||||
private void onBufferDrawKey(final Canvas canvas, final Key key) {
|
||||
final Paint paint = mPaint;
|
||||
final Drawable keyBackground = mKeyBackground;
|
||||
final Rect padding = mPadding;
|
||||
final int kbdPaddingLeft = getPaddingLeft();
|
||||
final int kbdPaddingTop = getPaddingTop();
|
||||
final int keyDrawX = key.mX + key.mVisualInsetsLeft;
|
||||
final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
|
||||
final int rowHeight = padding.top + key.mHeight;
|
||||
final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase();
|
||||
|
||||
canvas.translate(keyDrawX + kbdPaddingLeft, key.mY + kbdPaddingTop);
|
||||
|
||||
// Draw key background.
|
||||
final int[] drawableState = key.getCurrentDrawableState();
|
||||
keyBackground.setState(drawableState);
|
||||
final Rect bounds = keyBackground.getBounds();
|
||||
if (keyDrawWidth != bounds.right || key.mHeight != bounds.bottom) {
|
||||
keyBackground.setBounds(0, 0, keyDrawWidth, key.mHeight);
|
||||
}
|
||||
keyBackground.draw(canvas);
|
||||
|
||||
// Draw key label.
|
||||
if (key.mLabel != null) {
|
||||
// Switch the character to uppercase if shift is pressed
|
||||
final String label = key.mLabel == null ? null : adjustCase(key.mLabel).toString();
|
||||
// For characters, use large font. For labels like "Done", use small font.
|
||||
final int labelSize = getLabelSizeAndSetPaint(label, key.mLabelOption, paint);
|
||||
final int labelCharHeight = getLabelCharHeight(labelSize, paint);
|
||||
|
||||
// Vertical label text alignment.
|
||||
final float baseline;
|
||||
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_BOTTOM) != 0) {
|
||||
baseline = key.mHeight - labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR;
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
|
||||
new Paint());
|
||||
} else { // Align center
|
||||
final float centerY = (key.mHeight + padding.top - padding.bottom) / 2;
|
||||
baseline = centerY + labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER;
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
|
||||
new Paint());
|
||||
}
|
||||
// Horizontal label text alignment
|
||||
final int positionX;
|
||||
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
|
||||
positionX = mKeyLabelHorizontalPadding + padding.left;
|
||||
paint.setTextAlign(Align.LEFT);
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, new Paint());
|
||||
} else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
|
||||
positionX = keyDrawWidth - mKeyLabelHorizontalPadding - padding.right;
|
||||
paint.setTextAlign(Align.RIGHT);
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawVerticalLine(canvas, positionX, rowHeight, 0xc0808000, new Paint());
|
||||
} else {
|
||||
positionX = (keyDrawWidth + padding.left - padding.right) / 2;
|
||||
paint.setTextAlign(Align.CENTER);
|
||||
if (DEBUG_SHOW_ALIGN) {
|
||||
if (label.length() > 1)
|
||||
drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint());
|
||||
}
|
||||
}
|
||||
if (key.mManualTemporaryUpperCaseHintIcon != null && isManualTemporaryUpperCase) {
|
||||
paint.setColor(mKeyTextColorDisabled);
|
||||
} else {
|
||||
paint.setColor(mKeyTextColor);
|
||||
}
|
||||
if (key.mEnabled) {
|
||||
// Set a drop shadow for the text
|
||||
paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
|
||||
} else {
|
||||
// Make label invisible
|
||||
paint.setColor(Color.TRANSPARENT);
|
||||
}
|
||||
canvas.drawText(label, positionX, baseline, paint);
|
||||
// Turn off drop shadow
|
||||
paint.setShadowLayer(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Draw key icon.
|
||||
final Drawable icon = key.getIcon();
|
||||
if (key.mLabel == null && icon != null) {
|
||||
final int drawableWidth = icon.getIntrinsicWidth();
|
||||
final int drawableHeight = icon.getIntrinsicHeight();
|
||||
final int drawableX;
|
||||
final int drawableY = (key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
|
||||
if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
|
||||
drawableX = padding.left + mKeyLabelHorizontalPadding;
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawVerticalLine(canvas, drawableX, rowHeight, 0xc0800080, new Paint());
|
||||
} else if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) {
|
||||
drawableX = keyDrawWidth - padding.right - mKeyLabelHorizontalPadding
|
||||
- drawableWidth;
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawVerticalLine(canvas, drawableX + drawableWidth, rowHeight,
|
||||
0xc0808000, new Paint());
|
||||
} else { // Align center
|
||||
drawableX = (keyDrawWidth + padding.left - padding.right - drawableWidth) / 2;
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight,
|
||||
0xc0008080, new Paint());
|
||||
}
|
||||
drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight);
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
|
||||
0x80c00000, new Paint());
|
||||
}
|
||||
|
||||
// Draw hint icon.
|
||||
if (key.mHintIcon != null) {
|
||||
final int drawableWidth = keyDrawWidth;
|
||||
final int drawableHeight = key.mHeight;
|
||||
final int drawableX = 0;
|
||||
final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL;
|
||||
Drawable hintIcon = (isManualTemporaryUpperCase
|
||||
&& key.mManualTemporaryUpperCaseHintIcon != null)
|
||||
? key.mManualTemporaryUpperCaseHintIcon : key.mHintIcon;
|
||||
drawIcon(canvas, hintIcon, drawableX, drawableY, drawableWidth, drawableHeight);
|
||||
if (DEBUG_SHOW_ALIGN)
|
||||
drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
|
||||
0x80c0c000, new Paint());
|
||||
}
|
||||
|
||||
canvas.translate(-keyDrawX - kbdPaddingLeft, -key.mY - kbdPaddingTop);
|
||||
}
|
||||
|
||||
public int getLabelSizeAndSetPaint(CharSequence label, int keyLabelOption, Paint paint) {
|
||||
// For characters, use large font. For labels like "Done", use small font.
|
||||
final int labelSize;
|
||||
|
@ -1179,15 +1184,14 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
|||
|
||||
private PointerTracker getPointerTracker(final int id) {
|
||||
final ArrayList<PointerTracker> pointers = mPointerTrackers;
|
||||
final Key[] keys = mKeys;
|
||||
final KeyboardActionListener listener = mKeyboardActionListener;
|
||||
|
||||
// Create pointer trackers until we can get 'id+1'-th tracker, if needed.
|
||||
for (int i = pointers.size(); i <= id; i++) {
|
||||
final PointerTracker tracker =
|
||||
new PointerTracker(i, mHandler, mKeyDetector, this, getResources());
|
||||
if (keys != null)
|
||||
tracker.setKeyboard(mKeyboard, keys, mKeyHysteresisDistance);
|
||||
if (mKeyboard != null)
|
||||
tracker.setKeyboard(mKeyboard, mKeyHysteresisDistance);
|
||||
if (listener != null)
|
||||
tracker.setOnKeyboardActionListener(listener);
|
||||
pointers.add(tracker);
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
package com.android.inputmethod.keyboard;
|
||||
|
||||
public class MiniKeyboardKeyDetector extends KeyDetector {
|
||||
private static final int MAX_NEARBY_KEYS = 1;
|
||||
import java.util.List;
|
||||
|
||||
public class MiniKeyboardKeyDetector extends KeyDetector {
|
||||
private final int mSlideAllowanceSquare;
|
||||
private final int mSlideAllowanceSquareTop;
|
||||
|
||||
|
@ -31,20 +31,21 @@ public class MiniKeyboardKeyDetector extends KeyDetector {
|
|||
|
||||
@Override
|
||||
protected int getMaxNearbyKeys() {
|
||||
return MAX_NEARBY_KEYS;
|
||||
// No nearby key will be returned.
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
|
||||
final Key[] keys = getKeys();
|
||||
final List<Key> keys = getKeys();
|
||||
final int touchX = getTouchX(x);
|
||||
final int touchY = getTouchY(y);
|
||||
|
||||
int nearestIndex = NOT_A_KEY;
|
||||
int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
|
||||
final int keyCount = keys.length;
|
||||
final int keyCount = keys.size();
|
||||
for (int index = 0; index < keyCount; index++) {
|
||||
final int dist = keys[index].squaredDistanceToEdge(touchX, touchY);
|
||||
final int dist = keys.get(index).squaredDistanceToEdge(touchX, touchY);
|
||||
if (dist < nearestDist) {
|
||||
nearestIndex = index;
|
||||
nearestDist = dist;
|
||||
|
@ -52,7 +53,7 @@ public class MiniKeyboardKeyDetector extends KeyDetector {
|
|||
}
|
||||
|
||||
if (allCodes != null && nearestIndex != NOT_A_KEY)
|
||||
allCodes[0] = keys[nearestIndex].mCode;
|
||||
allCodes[0] = keys.get(nearestIndex).mCode;
|
||||
return nearestIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.util.Log;
|
|||
import android.view.MotionEvent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PointerTracker {
|
||||
private static final String TAG = PointerTracker.class.getSimpleName();
|
||||
|
@ -63,7 +64,7 @@ public class PointerTracker {
|
|||
private final int mTouchNoiseThresholdDistanceSquared;
|
||||
|
||||
private Keyboard mKeyboard;
|
||||
private Key[] mKeys;
|
||||
private List<Key> mKeys;
|
||||
private int mKeyHysteresisDistanceSquared = -1;
|
||||
private int mKeyQuarterWidthSquared;
|
||||
|
||||
|
@ -109,8 +110,8 @@ public class PointerTracker {
|
|||
public void onSwipeDown() {}
|
||||
};
|
||||
|
||||
public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy,
|
||||
Resources res) {
|
||||
public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector,
|
||||
UIProxy proxy, Resources res) {
|
||||
if (proxy == null || handler == null || keyDetector == null)
|
||||
throw new NullPointerException();
|
||||
mPointerId = id;
|
||||
|
@ -197,11 +198,11 @@ public class PointerTracker {
|
|||
mListener.onCancelInput();
|
||||
}
|
||||
|
||||
public void setKeyboard(Keyboard keyboard, Key[] keys, float keyHysteresisDistance) {
|
||||
if (keyboard == null || keys == null || keyHysteresisDistance < 0)
|
||||
public void setKeyboard(Keyboard keyboard, float keyHysteresisDistance) {
|
||||
if (keyboard == null || keyHysteresisDistance < 0)
|
||||
throw new IllegalArgumentException();
|
||||
mKeyboard = keyboard;
|
||||
mKeys = keys;
|
||||
mKeys = keyboard.getKeys();
|
||||
mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance);
|
||||
final int keyQuarterWidth = keyboard.getKeyWidth() / 4;
|
||||
mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
|
||||
|
@ -214,11 +215,11 @@ public class PointerTracker {
|
|||
}
|
||||
|
||||
private boolean isValidKeyIndex(int keyIndex) {
|
||||
return keyIndex >= 0 && keyIndex < mKeys.length;
|
||||
return keyIndex >= 0 && keyIndex < mKeys.size();
|
||||
}
|
||||
|
||||
public Key getKey(int keyIndex) {
|
||||
return isValidKeyIndex(keyIndex) ? mKeys[keyIndex] : null;
|
||||
return isValidKeyIndex(keyIndex) ? mKeys.get(keyIndex) : null;
|
||||
}
|
||||
|
||||
private static boolean isModifierCode(int primaryCode) {
|
||||
|
@ -258,12 +259,14 @@ public class PointerTracker {
|
|||
mPreviousKey = keyIndex;
|
||||
if (keyIndex != oldKeyIndex) {
|
||||
if (isValidKeyIndex(oldKeyIndex)) {
|
||||
mKeys[oldKeyIndex].onReleased();
|
||||
mProxy.invalidateKey(mKeys[oldKeyIndex]);
|
||||
final Key oldKey = mKeys.get(oldKeyIndex);
|
||||
oldKey.onReleased();
|
||||
mProxy.invalidateKey(oldKey);
|
||||
}
|
||||
if (isValidKeyIndex(keyIndex)) {
|
||||
mKeys[keyIndex].onPressed();
|
||||
mProxy.invalidateKey(mKeys[keyIndex]);
|
||||
final Key newKey = mKeys.get(keyIndex);
|
||||
newKey.onPressed();
|
||||
mProxy.invalidateKey(newKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -488,9 +491,6 @@ public class PointerTracker {
|
|||
if (!mIsRepeatableKey) {
|
||||
detectAndSendKey(keyIndex, x, y);
|
||||
}
|
||||
|
||||
if (isValidKeyIndex(keyIndex))
|
||||
mProxy.invalidateKey(mKeys[keyIndex]);
|
||||
}
|
||||
|
||||
public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
|
||||
|
@ -508,9 +508,6 @@ public class PointerTracker {
|
|||
mHandler.cancelPopupPreview();
|
||||
showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
|
||||
mIsInSlidingKeyInput = false;
|
||||
int keyIndex = mKeyState.getKeyIndex();
|
||||
if (isValidKeyIndex(keyIndex))
|
||||
mProxy.invalidateKey(mKeys[keyIndex]);
|
||||
}
|
||||
|
||||
public void repeatKey(int keyIndex) {
|
||||
|
@ -539,7 +536,7 @@ public class PointerTracker {
|
|||
if (newKey == curKey) {
|
||||
return true;
|
||||
} else if (isValidKeyIndex(curKey)) {
|
||||
return mKeys[curKey].squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared;
|
||||
return mKeys.get(curKey).squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Google Inc.
|
||||
*
|
||||
* 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;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ProximityKeyDetector extends KeyDetector {
|
||||
private static final String TAG = ProximityKeyDetector.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final int MAX_NEARBY_KEYS = 12;
|
||||
|
||||
// working area
|
||||
private final int[] mDistances = new int[MAX_NEARBY_KEYS];
|
||||
private final int[] mIndices = new int[MAX_NEARBY_KEYS];
|
||||
|
||||
@Override
|
||||
protected int getMaxNearbyKeys() {
|
||||
return MAX_NEARBY_KEYS;
|
||||
}
|
||||
|
||||
private void initializeNearbyKeys() {
|
||||
Arrays.fill(mDistances, Integer.MAX_VALUE);
|
||||
Arrays.fill(mIndices, NOT_A_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
|
||||
*
|
||||
* @param keyIndex index of the key.
|
||||
* @param distance distance between the key's edge and user touched point.
|
||||
* @return order of the key in the nearby buffer, 0 if it is the nearest key.
|
||||
*/
|
||||
private int sortNearbyKeys(int keyIndex, int distance) {
|
||||
final int[] distances = mDistances;
|
||||
final int[] indices = mIndices;
|
||||
for (int insertPos = 0; insertPos < distances.length; insertPos++) {
|
||||
if (distance < distances[insertPos]) {
|
||||
final int nextPos = insertPos + 1;
|
||||
if (nextPos < distances.length) {
|
||||
System.arraycopy(distances, insertPos, distances, nextPos,
|
||||
distances.length - nextPos);
|
||||
System.arraycopy(indices, insertPos, indices, nextPos,
|
||||
indices.length - nextPos);
|
||||
}
|
||||
distances[insertPos] = distance;
|
||||
indices[insertPos] = keyIndex;
|
||||
return insertPos;
|
||||
}
|
||||
}
|
||||
return distances.length;
|
||||
}
|
||||
|
||||
private void getNearbyKeyCodes(final int[] allCodes) {
|
||||
final Key[] keys = getKeys();
|
||||
final int[] indices = mIndices;
|
||||
|
||||
// allCodes[0] should always have the key code even if it is a non-letter key.
|
||||
if (indices[0] == NOT_A_KEY) {
|
||||
allCodes[0] = NOT_A_CODE;
|
||||
return;
|
||||
}
|
||||
|
||||
int numCodes = 0;
|
||||
for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) {
|
||||
final int index = indices[j];
|
||||
if (index == NOT_A_KEY)
|
||||
break;
|
||||
final int code = keys[index].mCode;
|
||||
// filter out a non-letter key from nearby keys
|
||||
if (code < Keyboard.CODE_SPACE)
|
||||
continue;
|
||||
allCodes[numCodes++] = code;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
|
||||
final Key[] keys = getKeys();
|
||||
final int touchX = getTouchX(x);
|
||||
final int touchY = getTouchY(y);
|
||||
|
||||
initializeNearbyKeys();
|
||||
int primaryIndex = NOT_A_KEY;
|
||||
for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) {
|
||||
final Key key = keys[index];
|
||||
final boolean isInside = key.isInside(touchX, touchY);
|
||||
final int distance = key.squaredDistanceToEdge(touchX, touchY);
|
||||
if (isInside || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
|
||||
final int insertedPosition = sortNearbyKeys(index, distance);
|
||||
if (insertedPosition == 0 && isInside)
|
||||
primaryIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
if (allCodes != null && allCodes.length > 0) {
|
||||
getNearbyKeyCodes(allCodes);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "x=" + x + " y=" + y
|
||||
+ " primary="
|
||||
+ (primaryIndex == NOT_A_KEY ? "none" : keys[primaryIndex].mCode)
|
||||
+ " codes=" + Arrays.toString(allCodes));
|
||||
}
|
||||
}
|
||||
|
||||
return primaryIndex;
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ import com.android.inputmethod.keyboard.Key;
|
|||
import com.android.inputmethod.keyboard.KeyDetector;
|
||||
import com.android.inputmethod.keyboard.KeyboardId;
|
||||
import com.android.inputmethod.keyboard.LatinKeyboard;
|
||||
import com.android.inputmethod.keyboard.ProximityKeyDetector;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
@ -38,7 +37,7 @@ public class SuggestHelper {
|
|||
// (and not try to find a dictionary provider for a specified locale)
|
||||
mSuggest = new Suggest(context, dictionaryId, null);
|
||||
mKeyboard = new LatinKeyboard(context, keyboardId);
|
||||
mKeyDetector = new ProximityKeyDetector();
|
||||
mKeyDetector = new KeyDetector();
|
||||
init();
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,7 @@ public class SuggestHelper {
|
|||
KeyboardId keyboardId) {
|
||||
mSuggest = new Suggest(dictionaryPath, startOffset, length, null);
|
||||
mKeyboard = new LatinKeyboard(context, keyboardId);
|
||||
mKeyDetector = new ProximityKeyDetector();
|
||||
mKeyDetector = new KeyDetector();
|
||||
init();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue