Merge change 20284
* changes:
Use multi-pointer support to improve 2-thumb typing. No more missed keys!
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 4d6b7be9..dff7cae 100755
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -202,7 +202,12 @@
private boolean mAbortKey;
private Key mInvalidatedKey;
private Rect mClipRegion = new Rect(0, 0, 0, 0);
-
+
+ // Variables for dealing with multiple pointers
+ private int mOldPointerCount = 1;
+ private float mOldPointerX;
+ private float mOldPointerY;
+
private Drawable mKeyBackground;
private static final int REPEAT_INTERVAL = 50; // ~20 keys per second
@@ -226,6 +231,8 @@
private Rect mDirtyRect = new Rect();
/** The keyboard bitmap for faster updates */
private Bitmap mBuffer;
+ /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */
+ private boolean mKeyboardChanged;
/** The canvas for the above mutable keyboard bitmap */
private Canvas mCanvas;
@@ -339,6 +346,7 @@
mPaint.setAntiAlias(true);
mPaint.setTextSize(keyTextSize);
mPaint.setTextAlign(Align.CENTER);
+ mPaint.setAlpha(255);
mPadding = new Rect(0, 0, 0, 0);
mMiniKeyboardCache = new HashMap<Key,View>();
@@ -404,9 +412,8 @@
List<Key> keys = mKeyboard.getKeys();
mKeys = keys.toArray(new Key[keys.size()]);
requestLayout();
- // Release buffer, just in case the new keyboard has a different size.
- // It will be reallocated on the next draw.
- mBuffer = null;
+ // Hint to reallocate the buffer if the size changed
+ mKeyboardChanged = true;
invalidateAllKeys();
computeProximityThreshold(keyboard);
mMiniKeyboardCache.clear(); // Not really necessary to do every time, but will free up views
@@ -566,17 +573,21 @@
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (mDrawPending || mBuffer == null) {
+ if (mDrawPending || mBuffer == null || mKeyboardChanged) {
onBufferDraw();
}
canvas.drawBitmap(mBuffer, 0, 0, null);
}
-
+
private void onBufferDraw() {
- if (mBuffer == null) {
- mBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
- mCanvas = new Canvas(mBuffer);
+ if (mBuffer == null || mKeyboardChanged) {
+ if (mBuffer == null || mKeyboardChanged &&
+ (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) {
+ mBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
+ mCanvas = new Canvas(mBuffer);
+ }
invalidateAllKeys();
+ mKeyboardChanged = false;
}
final Canvas canvas = mCanvas;
canvas.clipRect(mDirtyRect, Op.REPLACE);
@@ -592,7 +603,6 @@
final Key[] keys = mKeys;
final Key invalidKey = mInvalidatedKey;
- paint.setAlpha(255);
paint.setColor(mKeyTextColor);
boolean drawSingleKey = false;
if (invalidKey != null && canvas.getClipBounds(clipRegion)) {
@@ -613,7 +623,7 @@
}
int[] drawableState = key.getCurrentDrawableState();
keyBackground.setState(drawableState);
-
+
// Switch the character to uppercase if shift is pressed
String label = key.label == null? null : adjustCase(key.label).toString();
@@ -682,7 +692,6 @@
private int getKeyIndices(int x, int y, int[] allKeys) {
final Key[] keys = mKeys;
- final boolean shifted = mKeyboard.isShifted();
int primaryIndex = NOT_A_KEY;
int closestKey = NOT_A_KEY;
int closestKeyDist = mProximityThreshold + 1;
@@ -1013,15 +1022,48 @@
}
return false;
}
-
+
@Override
public boolean onTouchEvent(MotionEvent me) {
+ // Convert multi-pointer up/down events to single up/down events to
+ // deal with the typical multi-pointer behavior of two-thumb typing
+ int pointerCount = me.getPointerCount();
+ boolean result = false;
+ if (pointerCount != mOldPointerCount) {
+ long now = me.getEventTime();
+ if (pointerCount == 1) {
+ // Send a down event for the latest pointer
+ MotionEvent down = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN,
+ me.getX(), me.getY(), me.getMetaState());
+ result = onModifiedTouchEvent(down);
+ down.recycle();
+ } else {
+ // Send an up event for the last pointer
+ MotionEvent up = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP,
+ mOldPointerX, mOldPointerY, me.getMetaState());
+ result = onModifiedTouchEvent(up);
+ up.recycle();
+ }
+ } else {
+ if (pointerCount == 1) {
+ mOldPointerX = me.getX();
+ mOldPointerY = me.getY();
+ result = onModifiedTouchEvent(me);
+ } else {
+ // Don't do anything when 2 pointers are down and moving.
+ result = true;
+ }
+ }
+ mOldPointerCount = pointerCount;
+ return result;
+ }
+
+ private boolean onModifiedTouchEvent(MotionEvent me) {
int touchX = (int) me.getX() - mPaddingLeft;
int touchY = (int) me.getY() + mVerticalCorrection - mPaddingTop;
int action = me.getAction();
long eventTime = me.getEventTime();
int keyIndex = getKeyIndices(touchX, touchY, null);
-
if (mGestureDetector.onTouchEvent(me)) {
showPreview(NOT_A_KEY);
mHandler.removeMessages(MSG_REPEAT);