diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java | 939 |
1 files changed, 349 insertions, 590 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java index 4007c2b55..c449b36e7 100644 --- a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java +++ b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin; import android.content.Context; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -32,19 +33,20 @@ import android.inputmethodservice.Keyboard.Key; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; +import android.util.Log; import android.util.TypedValue; import android.view.GestureDetector; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; import android.view.ViewGroup.LayoutParams; import android.widget.PopupWindow; import android.widget.TextView; +import java.util.ArrayList; import java.util.HashMap; -import java.util.List; +import java.util.LinkedList; import java.util.Map; /** @@ -60,7 +62,11 @@ import java.util.Map; * @attr ref R.styleable#LatinKeyboardBaseView_verticalCorrection * @attr ref R.styleable#LatinKeyboardBaseView_popupLayout */ -public class LatinKeyboardBaseView extends View implements View.OnClickListener { +public class LatinKeyboardBaseView extends View implements PointerTracker.UIProxy { + private static final String TAG = "LatinKeyboardBaseView"; + private static final boolean DEBUG = false; + + public static final int NOT_A_TOUCH_COORDINATE = -1; public interface OnKeyboardActionListener { @@ -139,116 +145,97 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener void swipeUp(); } - public static final int NOT_A_TOUCH_COORDINATE = -1; + // Timing constants + private static final int DELAY_BEFORE_PREVIEW = 0; + private static final int DELAY_AFTER_PREVIEW = 70; + private static final int REPEAT_INTERVAL = PointerTracker.REPEAT_INTERVAL; - private static final boolean DEBUG = false; - static final int NOT_A_KEY = -1; - private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE }; + // Miscellaneous constants + /* package */ static final int NOT_A_KEY = -1; private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable }; - private Keyboard mKeyboard; - private int mCurrentKeyIndex = NOT_A_KEY; - private int mLabelTextSize; + // XML attribute private int mKeyTextSize; private int mKeyTextColor; - private float mShadowRadius; + private Typeface mKeyTextStyle = Typeface.DEFAULT; + private int mLabelTextSize; + private int mSymbolColorScheme = 0; private int mShadowColor; + private float mShadowRadius; + private Drawable mKeyBackground; private float mBackgroundDimAmount; - - private TextView mPreviewText; - private PopupWindow mPreviewPopup; - private int mPreviewTextSizeLarge; + private float mVerticalCorrection; private int mPreviewOffset; private int mPreviewHeight; - private int[] mOffsetInWindow; + private int mPopupLayout; - private PopupWindow mPopupKeyboard; - private View mMiniKeyboardContainer; - private LatinKeyboardBaseView mMiniKeyboard; - private boolean mMiniKeyboardOnScreen; - private View mPopupParent; - private int mMiniKeyboardOffsetX; - private int mMiniKeyboardOffsetY; - private Map<Key,View> mMiniKeyboardCache; - private int[] mWindowOffset; + // Main keyboard + private Keyboard mKeyboard; private Key[] mKeys; - private Typeface mKeyTextStyle = Typeface.DEFAULT; - private int mSymbolColorScheme = 0; - /** Listener for {@link OnKeyboardActionListener}. */ - private OnKeyboardActionListener mKeyboardActionListener; - - private static final int DELAY_BEFORE_PREVIEW = 0; - private static final int DELAY_AFTER_PREVIEW = 70; - private static final int DEBOUNCE_TIME = 70; - - private int mVerticalCorrection; - private ProximityKeyDetector mProximityKeyDetector = new ProximityKeyDetector(); - - private boolean mPreviewCentered = false; + // Key preview popup + private final static boolean PREVIEW_CENTERED = false; + private TextView mPreviewText; + private PopupWindow mPreviewPopup; + private int mPreviewTextSizeLarge; + private int[] mOffsetInWindow; + private int mOldPreviewKeyIndex = NOT_A_KEY; private boolean mShowPreview = true; private boolean mShowTouchPoints = true; private int mPopupPreviewX; private int mPopupPreviewY; + private int mPopupPreviewOffsetX; + private int mPopupPreviewOffsetY; private int mWindowY; - private Paint mPaint; - private Rect mPadding; - - private int mCurrentKey = NOT_A_KEY; - private int mStartX; - private int mStartY; - - private KeyDebouncer mDebouncer; - - private GestureDetector mGestureDetector; - private int mPopupX; - private int mPopupY; - private int mPopupLayout; - private boolean mAbortKey; - private Key mInvalidatedKey; - private Rect mClipRegion = new Rect(0, 0, 0, 0); - private SwipeTracker mSwipeTracker = new SwipeTracker(); - private int mSwipeThreshold; - private boolean mDisambiguateSwipe; + // Popup mini keyboard + private PopupWindow mMiniKeyboardPopup; + private LatinKeyboardBaseView mMiniKeyboard; + private View mMiniKeyboardParent; + private Map<Key,View> mMiniKeyboardCache; + private int mMiniKeyboardOriginX; + private int mMiniKeyboardOriginY; + private long mMiniKeyboardPopupTime; + private int[] mWindowOffset; - // Variables for dealing with multiple pointers - private int mOldPointerCount = 1; - private int mOldPointerX; - private int mOldPointerY; + /** Listener for {@link OnKeyboardActionListener}. */ + private OnKeyboardActionListener mKeyboardActionListener; - private Drawable mKeyBackground; + private final ArrayList<PointerTracker> mPointerTrackers = new ArrayList<PointerTracker>(); + private final PointerQueue mPointerQueue = new PointerQueue(); + private final float mDebounceHysteresis; - private static final int REPEAT_INTERVAL = 50; // ~20 keys per second - private static final int REPEAT_START_DELAY = 400; - private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); + protected KeyDetector mKeyDetector = new ProximityKeyDetector(); - // For multi-tap - private int mLastSentIndex; - private int mTapCount; - private long mLastTapTime; - private boolean mInMultiTap; - private static final int MULTITAP_INTERVAL = 800; // milliseconds - private StringBuilder mPreviewLabel = new StringBuilder(1); + // Swipe gesture detector + private final GestureDetector mGestureDetector; + private final SwipeTracker mSwipeTracker = new SwipeTracker(); + private final int mSwipeThreshold; + private final boolean mDisambiguateSwipe; + // Drawing /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/ private boolean mDrawPending; /** The dirty region in the keyboard bitmap */ - private Rect mDirtyRect = new Rect(); + private final 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; + private Key mInvalidatedKey; /** The canvas for the above mutable keyboard bitmap */ private Canvas mCanvas; + private final Paint mPaint; + private final Rect mPadding; + private final Rect mClipRegion = new Rect(0, 0, 0, 0); - UIHandler mHandler = new UIHandler(); + private final UIHandler mHandler = new UIHandler(); class UIHandler extends Handler { private static final int MSG_POPUP_PREVIEW = 1; private static final int MSG_DISMISS_PREVIEW = 2; private static final int MSG_REPEAT_KEY = 3; - private static final int MSG_LOGPRESS_KEY = 4; + private static final int MSG_LONGPRESS_KEY = 4; private boolean mInKeyRepeat; @@ -256,24 +243,34 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener public void handleMessage(Message msg) { switch (msg.what) { case MSG_POPUP_PREVIEW: - showKey(msg.arg1); + showKey(msg.arg1, (PointerTracker)msg.obj); break; case MSG_DISMISS_PREVIEW: mPreviewText.setVisibility(INVISIBLE); break; - case MSG_REPEAT_KEY: - repeatKey(msg.arg1); - startKeyRepeatTimer(REPEAT_INTERVAL, msg.arg1); + case MSG_REPEAT_KEY: { + final PointerTracker tracker = (PointerTracker)msg.obj; + tracker.repeatKey(msg.arg1); + startKeyRepeatTimer(REPEAT_INTERVAL, msg.arg1, tracker); break; - case MSG_LOGPRESS_KEY: - openPopupIfRequired(msg.arg1); + } + case MSG_LONGPRESS_KEY: { + final PointerTracker tracker = (PointerTracker)msg.obj; + openPopupIfRequired(msg.arg1, tracker); break; + } } } - public void popupPreview(int keyIndex, long delay) { + public void popupPreview(long delay, int keyIndex, PointerTracker tracker) { removeMessages(MSG_POPUP_PREVIEW); - sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0), delay); + if (mPreviewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) { + // Show right away, if it's already visible and finger is moving around + showKey(keyIndex, tracker); + } else { + sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker), + delay); + } } public void cancelPopupPreview() { @@ -281,16 +278,18 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener } public void dismissPreview(long delay) { - sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay); + if (mPreviewPopup.isShowing()) { + sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay); + } } public void cancelDismissPreview() { removeMessages(MSG_DISMISS_PREVIEW); } - public void startKeyRepeatTimer(long delay, int keyIndex) { + public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) { mInKeyRepeat = true; - sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0), delay); + sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0, tracker), delay); } public void cancelKeyRepeatTimer() { @@ -302,13 +301,13 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener return mInKeyRepeat; } - public void startLongPressTimer(int keyIndex, long delay) { - removeMessages(MSG_LOGPRESS_KEY); - sendMessageDelayed(obtainMessage(MSG_LOGPRESS_KEY, keyIndex, 0), delay); + public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) { + removeMessages(MSG_LONGPRESS_KEY); + sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0, tracker), delay); } public void cancelLongPressTimer() { - removeMessages(MSG_LOGPRESS_KEY); + removeMessages(MSG_LONGPRESS_KEY); } public void cancelKeyTimers() { @@ -323,109 +322,38 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener } }; - static class KeyDebouncer { - private final Key[] mKeys; - private final int mKeyDebounceThresholdSquared; - - // for move de-bouncing - private int mLastCodeX; - private int mLastCodeY; - private int mLastX; - private int mLastY; - - // for time de-bouncing - private int mLastKey; - private long mLastKeyTime; - private long mLastMoveTime; - private long mCurrentKeyTime; - - KeyDebouncer(Key[] keys, float hysteresisPixel) { - if (keys == null || hysteresisPixel < 1.0f) - throw new IllegalArgumentException(); - mKeys = keys; - mKeyDebounceThresholdSquared = (int)(hysteresisPixel * hysteresisPixel); - } - - public int getLastCodeX() { - return mLastCodeX; - } - - public int getLastCodeY() { - return mLastCodeY; - } - - public int getLastX() { - return mLastX; - } - - public int getLastY() { - return mLastY; - } - - public int getLastKey() { - return mLastKey; - } - - public void startMoveDebouncing(int x, int y) { - mLastCodeX = x; - mLastCodeY = y; - } + static class PointerQueue { + private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>(); - public void updateMoveDebouncing(int x, int y) { - mLastX = x; - mLastY = y; + public void add(PointerTracker tracker) { + mQueue.add(tracker); } - public void resetMoveDebouncing() { - mLastCodeX = mLastX; - mLastCodeY = mLastY; - } - - public boolean isMinorMoveBounce(int x, int y, int newKey, int curKey) { - if (newKey == curKey) { - return true; - } else if (curKey >= 0 && curKey < mKeys.length) { - return getSquareDistanceToKeyEdge(x, y, mKeys[curKey]) - < mKeyDebounceThresholdSquared; - } else { - return false; + public int lastIndexOf(PointerTracker tracker) { + LinkedList<PointerTracker> queue = mQueue; + for (int index = queue.size() - 1; index >= 0; index--) { + PointerTracker t = queue.get(index); + if (t == tracker) + return index; } + return -1; } - private static int getSquareDistanceToKeyEdge(int x, int y, Key key) { - final int left = key.x; - final int right = key.x + key.width; - final int top = key.y; - final int bottom = key.y + key.height; - final int edgeX = x < left ? left : (x > right ? right : x); - final int edgeY = y < top ? top : (y > bottom ? bottom : y); - final int dx = x - edgeX; - final int dy = y - edgeY; - return dx * dx + dy * dy; - } - - public void startTimeDebouncing(long eventTime) { - mLastKey = NOT_A_KEY; - mLastKeyTime = 0; - mCurrentKeyTime = 0; - mLastMoveTime = eventTime; - } - - public void updateTimeDebouncing(long eventTime) { - mCurrentKeyTime += eventTime - mLastMoveTime; - mLastMoveTime = eventTime; - } - - public void resetTimeDebouncing(long eventTime, int currentKey) { - mLastKey = currentKey; - mLastKeyTime = mCurrentKeyTime + eventTime - mLastMoveTime; - mCurrentKeyTime = 0; - mLastMoveTime = eventTime; + public void releasePointersOlderThan(PointerTracker tracker, long eventTime) { + LinkedList<PointerTracker> queue = mQueue; + int oldestPos = 0; + for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) { + if (t.isModifier()) { + oldestPos++; + } else { + t.onUpEvent(t.getLastX(), t.getLastY(), eventTime); + queue.remove(oldestPos); + } + } } - public boolean isMinorTimeBounce() { - return mCurrentKeyTime < mLastKeyTime && mCurrentKeyTime < DEBOUNCE_TIME - && mLastKey != NOT_A_KEY; + public void remove(PointerTracker tracker) { + mQueue.remove(tracker); } } @@ -516,15 +444,11 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener } else { mShowPreview = false; } - mPreviewPopup.setTouchable(false); + mMiniKeyboardParent = this; - mPopupKeyboard = new PopupWindow(context); - mPopupKeyboard.setBackgroundDrawable(null); - //mPopupKeyboard.setClippingEnabled(false); - - mPopupParent = this; - //mPredicting = true; + mMiniKeyboardPopup = new PopupWindow(context); + mMiniKeyboardPopup.setBackgroundDrawable(null); mPaint = new Paint(); mPaint.setAntiAlias(true); @@ -536,19 +460,17 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener mMiniKeyboardCache = new HashMap<Key,View>(); mKeyBackground.getPadding(mPadding); - mSwipeThreshold = (int) (500 * getResources().getDisplayMetrics().density); + final Resources res = getResources(); + mSwipeThreshold = (int) (500 * res.getDisplayMetrics().density); // TODO: Refer frameworks/base/core/res/res/values/config.xml - mDisambiguateSwipe = getResources().getBoolean(R.bool.config_swipeDisambiguation); - resetMultiTap(); - initGestureDetector(); - } + mDisambiguateSwipe = res.getBoolean(R.bool.config_swipeDisambiguation); + mDebounceHysteresis = res.getDimension(R.dimen.key_debounce_hysteresis_distance); - private void initGestureDetector() { GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener() { @Override - public boolean onFling(MotionEvent me1, MotionEvent me2, - float velocityX, float velocityY) { + public boolean onFling(MotionEvent me1, MotionEvent me2, float velocityX, + float velocityY) { final float absX = Math.abs(velocityX); final float absY = Math.abs(velocityY); float deltaX = me2.getX() - me1.getX(); @@ -590,6 +512,9 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener public void setOnKeyboardActionListener(OnKeyboardActionListener listener) { mKeyboardActionListener = listener; + for (PointerTracker tracker : mPointerTrackers) { + tracker.setOnKeyboardActionListener(listener); + } } /** @@ -609,26 +534,24 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener */ public void setKeyboard(Keyboard keyboard) { if (mKeyboard != null) { - showPreview(NOT_A_KEY); + dismissKeyPreview(); } // Remove any pending messages, except dismissing preview mHandler.cancelKeyTimers(); mHandler.cancelPopupPreview(); mKeyboard = keyboard; - LatinImeLogger.onSetKeyboard(mKeyboard); - List<Key> keys = mKeyboard.getKeys(); - mKeys = keys.toArray(new Key[keys.size()]); - mProximityKeyDetector.setKeyboard(keyboard, mKeys); + LatinImeLogger.onSetKeyboard(keyboard); + mKeys = mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), + -getPaddingTop() + mVerticalCorrection); + for (PointerTracker tracker : mPointerTrackers) { + tracker.setKeyboard(mKeys, mDebounceHysteresis); + } requestLayout(); // 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 - // Switching to a different keyboard should abort any pending keys so that the key up - // doesn't get delivered to the old or new keyboard - mAbortKey = true; // Until the next ACTION_DOWN } /** @@ -691,16 +614,13 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener return mSymbolColorScheme; } - public void setVerticalCorrection(int verticalOffset) { - } - public void setPopupParent(View v) { - mPopupParent = v; + mMiniKeyboardParent = v; } public void setPopupOffset(int x, int y) { - mMiniKeyboardOffsetX = x; - mMiniKeyboardOffsetY = y; + mPopupPreviewOffsetX = x; + mPopupPreviewOffsetY = y; if (mPreviewPopup.isShowing()) { mPreviewPopup.dismiss(); } @@ -713,22 +633,14 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener * @param enabled whether or not the proximity correction is enabled */ public void setProximityCorrectionEnabled(boolean enabled) { - mProximityKeyDetector.setProximityCorrectionEnabled(enabled); + mKeyDetector.setProximityCorrectionEnabled(enabled); } /** * Returns true if proximity correction is enabled. */ public boolean isProximityCorrectionEnabled() { - return mProximityKeyDetector.isProximityCorrectionEnabled(); - } - - /** - * Popup keyboard close button clicked. - * @hide - */ - public void onClick(View v) { - dismissPopupKeyboard(); + return mKeyDetector.isProximityCorrectionEnabled(); } protected CharSequence adjustCase(CharSequence label) { @@ -772,11 +684,7 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener dimensionSum += Math.min(key.width, key.height) + key.gap; } if (dimensionSum < 0 || length == 0) return; - mProximityKeyDetector.setProximityThreshold((int) (dimensionSum * 1.4f / length)); - - final float hysteresisPixel = getContext().getResources() - .getDimension(R.dimen.key_debounce_hysteresis_distance); - mDebouncer = new KeyDebouncer(keys, hysteresisPixel); + mKeyDetector.setProximityThreshold((int) (dimensionSum * 1.4f / length)); } @Override @@ -889,23 +797,27 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener } mInvalidatedKey = null; // Overlay a dark rectangle to dim the keyboard - if (mMiniKeyboardOnScreen) { + if (mMiniKeyboard != null) { paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24); canvas.drawRect(0, 0, getWidth(), getHeight(), paint); } if (DEBUG) { if (mShowTouchPoints) { - int lastX = mDebouncer.getLastX(); - int lastY = mDebouncer.getLastY(); - paint.setAlpha(128); - paint.setColor(0xFFFF0000); - canvas.drawCircle(mStartX, mStartY, 3, paint); - canvas.drawLine(mStartX, mStartY, lastX, lastY, paint); - paint.setColor(0xFF0000FF); - canvas.drawCircle(lastX, lastY, 3, paint); - paint.setColor(0xFF00FF00); - canvas.drawCircle((mStartX + lastX) / 2, (mStartY + lastY) / 2, 2, paint); + for (PointerTracker tracker : mPointerTrackers) { + int startX = tracker.getStartX(); + int startY = tracker.getStartY(); + int lastX = tracker.getLastX(); + int lastY = tracker.getLastY(); + paint.setAlpha(128); + paint.setColor(0xFFFF0000); + canvas.drawCircle(startX, startY, 3, paint); + canvas.drawLine(startX, startY, lastX, lastY, paint); + paint.setColor(0xFF0000FF); + canvas.drawCircle(lastX, lastY, 3, paint); + paint.setColor(0xFF00FF00); + canvas.drawCircle((startX + lastX) / 2, (startY + lastY) / 2, 2, paint); + } } } @@ -913,105 +825,39 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener mDirtyRect.setEmpty(); } - - private void detectAndSendKey(int index, int x, int y, long eventTime) { - if (index != NOT_A_KEY && index < mKeys.length) { - final Key key = mKeys[index]; - if (key.text != null) { - mKeyboardActionListener.onText(key.text); - mKeyboardActionListener.onRelease(NOT_A_KEY); - } else { - int code = key.codes[0]; - //TextEntryState.keyPressedAt(key, x, y); - int[] codes = mProximityKeyDetector.newCodeArray(); - mProximityKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes); - // Multi-tap - if (mInMultiTap) { - if (mTapCount != -1) { - mKeyboardActionListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE, x, y); - } else { - mTapCount = 0; - } - code = key.codes[mTapCount]; - } - /* - * Swap the first and second values in the codes array if the primary code is not - * the first value but the second value in the array. This happens when key - * debouncing is in effect. - */ - if (codes.length >= 2 && codes[0] != code && codes[1] == code) { - codes[1] = codes[0]; - codes[0] = code; - } - mKeyboardActionListener.onKey(code, codes, x, y); - mKeyboardActionListener.onRelease(code); - } - mLastSentIndex = index; - mLastTapTime = eventTime; - } + // TODO: clean up this method. + private void dismissKeyPreview() { + for (PointerTracker tracker : mPointerTrackers) + tracker.updateKey(NOT_A_KEY); + showPreview(NOT_A_KEY, null); } - /** - * Handle multi-tap keys by producing the key label for the current multi-tap state. - */ - private CharSequence getPreviewText(Key key) { - if (mInMultiTap) { - // Multi-tap - mPreviewLabel.setLength(0); - mPreviewLabel.append((char) key.codes[mTapCount < 0 ? 0 : mTapCount]); - return adjustCase(mPreviewLabel); - } else { - return adjustCase(key.label); - } - } - - private void showPreview(int keyIndex) { - int oldKeyIndex = mCurrentKeyIndex; - final PopupWindow previewPopup = mPreviewPopup; - - mCurrentKeyIndex = keyIndex; - // Release the old key and press the new key - final Key[] keys = mKeys; - if (oldKeyIndex != mCurrentKeyIndex) { - if (oldKeyIndex != NOT_A_KEY && keys.length > oldKeyIndex) { - keys[oldKeyIndex].onReleased(mCurrentKeyIndex == NOT_A_KEY); - invalidateKey(oldKeyIndex); - } - if (mCurrentKeyIndex != NOT_A_KEY && keys.length > mCurrentKeyIndex) { - keys[mCurrentKeyIndex].onPressed(); - invalidateKey(mCurrentKeyIndex); - } - } + public void showPreview(int keyIndex, PointerTracker tracker) { + int oldKeyIndex = mOldPreviewKeyIndex; + mOldPreviewKeyIndex = keyIndex; // If key changed and preview is on ... - if (oldKeyIndex != mCurrentKeyIndex && mShowPreview) { + if (oldKeyIndex != keyIndex && mShowPreview) { if (keyIndex == NOT_A_KEY) { mHandler.cancelPopupPreview(); - if (previewPopup.isShowing()) { - mHandler.dismissPreview(DELAY_AFTER_PREVIEW); - } - } else { - if (previewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) { - // Show right away, if it's already visible and finger is moving around - showKey(keyIndex); - } else { - mHandler.popupPreview(keyIndex, DELAY_BEFORE_PREVIEW); - } + mHandler.dismissPreview(DELAY_AFTER_PREVIEW); + } else if (tracker != null) { + mHandler.popupPreview(DELAY_BEFORE_PREVIEW, keyIndex, tracker); } } } - private void showKey(final int keyIndex) { + private void showKey(final int keyIndex, PointerTracker tracker) { + Key key = tracker.getKey(keyIndex); + if (key == null) + return; final PopupWindow previewPopup = mPreviewPopup; - final Key[] keys = mKeys; - if (keyIndex < 0 || keyIndex >= mKeys.length) return; - Key key = keys[keyIndex]; if (key.icon != null) { mPreviewText.setCompoundDrawables(null, null, null, key.iconPreview != null ? key.iconPreview : key.icon); mPreviewText.setText(null); } else { mPreviewText.setCompoundDrawables(null, null, null, null); - mPreviewText.setText(getPreviewText(key)); + mPreviewText.setText(adjustCase(tracker.getPreviewText(key))); if (key.label.length() > 1 && key.codes.length < 2) { mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize); mPreviewText.setTypeface(Typeface.DEFAULT_BOLD); @@ -1030,20 +876,20 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener lp.width = popupWidth; lp.height = popupHeight; } - if (!mPreviewCentered) { - mPopupPreviewX = key.x - mPreviewText.getPaddingLeft() + getPaddingLeft(); - mPopupPreviewY = key.y - popupHeight + mPreviewOffset; - } else { + if (PREVIEW_CENTERED) { // TODO: Fix this if centering is brought back mPopupPreviewX = 160 - mPreviewText.getMeasuredWidth() / 2; mPopupPreviewY = - mPreviewText.getMeasuredHeight(); + } else { + mPopupPreviewX = key.x - mPreviewText.getPaddingLeft() + getPaddingLeft(); + mPopupPreviewY = key.y - popupHeight + mPreviewOffset; } mHandler.cancelDismissPreview(); if (mOffsetInWindow == null) { mOffsetInWindow = new int[2]; getLocationInWindow(mOffsetInWindow); - mOffsetInWindow[0] += mMiniKeyboardOffsetX; // Offset may be zero - mOffsetInWindow[1] += mMiniKeyboardOffsetY; // Offset may be zero + mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero + mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero int[] mWindowLocation = new int[2]; getLocationOnScreen(mWindowLocation); mWindowY = mWindowLocation[1]; @@ -1072,7 +918,7 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener } else { previewPopup.setWidth(popupWidth); previewPopup.setHeight(popupHeight); - previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY, + previewPopup.showAtLocation(mMiniKeyboardParent, Gravity.NO_GRAVITY, mPopupPreviewX, mPopupPreviewY); } mPreviewText.setVisibility(VISIBLE); @@ -1082,7 +928,7 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient * because the keyboard renders the keys to an off-screen buffer and an invalidate() only * draws the cached buffer. - * @see #invalidateKey(int) + * @see #invalidateKey(Key) */ public void invalidateAllKeys() { mDirtyRect.union(0, 0, getWidth(), getHeight()); @@ -1094,15 +940,12 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only * one key is changing it's content. Any changes that affect the position or size of the key * may not be honored. - * @param keyIndex the index of the key in the attached {@link Keyboard}. + * @param key key in the attached {@link Keyboard}. * @see #invalidateAllKeys */ - public void invalidateKey(int keyIndex) { - if (mKeys == null) return; - if (keyIndex < 0 || keyIndex >= mKeys.length) { + public void invalidateKey(Key key) { + if (key == null) return; - } - final Key key = mKeys[keyIndex]; mInvalidatedKey = key; mDirtyRect.union(key.x + getPaddingLeft(), key.y + getPaddingTop(), key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); @@ -1111,24 +954,75 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); } - private boolean openPopupIfRequired(int keyIndex) { + private boolean openPopupIfRequired(int keyIndex, PointerTracker tracker) { // Check if we have a popup layout specified first. if (mPopupLayout == 0) { return false; } - if (keyIndex < 0 || keyIndex >= mKeys.length) { - return false; - } - Key popupKey = mKeys[keyIndex]; + Key popupKey = tracker.getKey(keyIndex); + if (popupKey == null) + return false; boolean result = onLongPress(popupKey); if (result) { - mAbortKey = true; - showPreview(NOT_A_KEY); + dismissKeyPreview(); + tracker.setAlreadyProcessed(); } return result; } + private View inflateMiniKeyboardContainer(Key popupKey) { + int popupKeyboardId = popupKey.popupResId; + LayoutInflater inflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + View container = inflater.inflate(mPopupLayout, null); + if (container == null) + throw new NullPointerException(); + + mMiniKeyboard = (LatinKeyboardBaseView)container.findViewById(R.id.LatinKeyboardBaseView); + mMiniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() { + public void onKey(int primaryCode, int[] keyCodes, int x, int y) { + mKeyboardActionListener.onKey(primaryCode, keyCodes, x, y); + dismissPopupKeyboard(); + } + + public void onText(CharSequence text) { + mKeyboardActionListener.onText(text); + dismissPopupKeyboard(); + } + + public void swipeLeft() { + } + public void swipeRight() { + } + public void swipeUp() { + } + public void swipeDown() { + } + public void onPress(int primaryCode) { + mKeyboardActionListener.onPress(primaryCode); + } + public void onRelease(int primaryCode) { + mKeyboardActionListener.onRelease(primaryCode); + } + }); + + Keyboard keyboard; + if (popupKey.popupCharacters != null) { + keyboard = new Keyboard(getContext(), popupKeyboardId, popupKey.popupCharacters, + -1, getPaddingLeft() + getPaddingRight()); + } else { + keyboard = new Keyboard(getContext(), popupKeyboardId); + } + mMiniKeyboard.setKeyboard(keyboard); + mMiniKeyboard.setPopupParent(this); + + container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); + + return container; + } + /** * Called when a key is long pressed. By default this will open any popup keyboard associated * with this key through the attributes popupLayout and popupCharacters. @@ -1137,123 +1031,99 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener * method on the base class if the subclass doesn't wish to handle the call. */ protected boolean onLongPress(Key popupKey) { - int popupKeyboardId = popupKey.popupResId; + // TODO if popupKey.popupCharacters has only one letter, send it as key without opening + // mini keyboard. - if (popupKeyboardId != 0) { - mMiniKeyboardContainer = mMiniKeyboardCache.get(popupKey); - if (mMiniKeyboardContainer == null) { - LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - mMiniKeyboardContainer = inflater.inflate(mPopupLayout, null); - mMiniKeyboard = (LatinKeyboardBaseView) mMiniKeyboardContainer.findViewById( - R.id.LatinKeyboardBaseView); - View closeButton = mMiniKeyboardContainer.findViewById( - R.id.closeButton); - if (closeButton != null) closeButton.setOnClickListener(this); - mMiniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() { - public void onKey(int primaryCode, int[] keyCodes, int x, int y) { - mKeyboardActionListener.onKey(primaryCode, keyCodes, x, y); - dismissPopupKeyboard(); - } + if (popupKey.popupResId == 0) + return false; - public void onText(CharSequence text) { - mKeyboardActionListener.onText(text); - dismissPopupKeyboard(); - } + View container = mMiniKeyboardCache.get(popupKey); + if (container == null) { + container = inflateMiniKeyboardContainer(popupKey); + mMiniKeyboardCache.put(popupKey, container); + } + mMiniKeyboard = (LatinKeyboardBaseView)container.findViewById(R.id.LatinKeyboardBaseView); + if (mWindowOffset == null) { + mWindowOffset = new int[2]; + getLocationInWindow(mWindowOffset); + } + int popupX = popupKey.x + popupKey.width + getPaddingLeft(); + int popupY = popupKey.y + getPaddingTop(); + popupX -= container.getMeasuredWidth(); + popupY -= container.getMeasuredHeight(); + popupX += mWindowOffset[0]; + popupY += mWindowOffset[1]; + final int x = popupX + container.getPaddingRight(); + final int y = popupY + container.getPaddingBottom(); + mMiniKeyboardOriginX = (x < 0 ? 0 : x) + container.getPaddingLeft(); + mMiniKeyboardOriginY = y + container.getPaddingTop(); + mMiniKeyboard.setPopupOffset((x < 0) ? 0 : x, y); + mMiniKeyboard.setShifted(isShifted()); + mMiniKeyboard.setPreviewEnabled(isPreviewEnabled()); + mMiniKeyboardPopup.setContentView(container); + mMiniKeyboardPopup.setWidth(container.getMeasuredWidth()); + mMiniKeyboardPopup.setHeight(container.getMeasuredHeight()); + mMiniKeyboardPopup.showAtLocation(this, Gravity.NO_GRAVITY, x, y); + + // Inject down event on the key to mini keyboard. + long eventTime = System.currentTimeMillis(); + mMiniKeyboardPopupTime = eventTime; + MotionEvent downEvent = generateMiniKeyboardMotionEvent(MotionEvent.ACTION_DOWN, popupKey.x + + popupKey.width / 2, popupKey.y + popupKey.height / 2, eventTime); + mMiniKeyboard.onTouchEvent(downEvent); + downEvent.recycle(); - public void swipeLeft() { } - public void swipeRight() { } - public void swipeUp() { } - public void swipeDown() { } - public void onPress(int primaryCode) { - mKeyboardActionListener.onPress(primaryCode); - } - public void onRelease(int primaryCode) { - mKeyboardActionListener.onRelease(primaryCode); - } - }); - //mInputView.setSuggest(mSuggest); - Keyboard keyboard; - if (popupKey.popupCharacters != null) { - keyboard = new Keyboard(getContext(), popupKeyboardId, - popupKey.popupCharacters, -1, getPaddingLeft() + getPaddingRight()); - } else { - keyboard = new Keyboard(getContext(), popupKeyboardId); - } - mMiniKeyboard.setKeyboard(keyboard); - mMiniKeyboard.setPopupParent(this); - mMiniKeyboardContainer.measure( - MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), - MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); - - mMiniKeyboardCache.put(popupKey, mMiniKeyboardContainer); - } else { - mMiniKeyboard = (LatinKeyboardBaseView) mMiniKeyboardContainer.findViewById( - R.id.LatinKeyboardBaseView); - } - if (mWindowOffset == null) { - mWindowOffset = new int[2]; - getLocationInWindow(mWindowOffset); - } - mPopupX = popupKey.x + getPaddingLeft(); - mPopupY = popupKey.y + getPaddingTop(); - mPopupX = mPopupX + popupKey.width - mMiniKeyboardContainer.getMeasuredWidth(); - mPopupY = mPopupY - mMiniKeyboardContainer.getMeasuredHeight(); - final int x = mPopupX + mMiniKeyboardContainer.getPaddingRight() + mWindowOffset[0]; - final int y = mPopupY + mMiniKeyboardContainer.getPaddingBottom() + mWindowOffset[1]; - mMiniKeyboard.setPopupOffset(x < 0 ? 0 : x, y); - mMiniKeyboard.setShifted(isShifted()); - mPopupKeyboard.setContentView(mMiniKeyboardContainer); - mPopupKeyboard.setWidth(mMiniKeyboardContainer.getMeasuredWidth()); - mPopupKeyboard.setHeight(mMiniKeyboardContainer.getMeasuredHeight()); - mPopupKeyboard.showAtLocation(this, Gravity.NO_GRAVITY, x, y); - mMiniKeyboardOnScreen = true; - //mMiniKeyboard.onTouchEvent(getTranslatedEvent(me)); - invalidateAllKeys(); - return true; - } - return false; + invalidateAllKeys(); + return true; } - private int getTouchX(float x) { - return (int)x - getPaddingLeft(); + private MotionEvent generateMiniKeyboardMotionEvent(int action, int x, int y, long eventTime) { + return MotionEvent.obtain(mMiniKeyboardPopupTime, eventTime, action, + x - mMiniKeyboardOriginX, y - mMiniKeyboardOriginY, 0); } - private int getTouchY(float y) { - return (int)y + mVerticalCorrection - getPaddingTop(); + private PointerTracker getPointerTracker(final int id) { + final ArrayList<PointerTracker> pointers = mPointerTrackers; + final Key[] keys = mKeys; + final OnKeyboardActionListener 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); + if (keys != null) + tracker.setKeyboard(keys, mDebounceHysteresis); + if (listener != null) + tracker.setOnKeyboardActionListener(listener); + pointers.add(tracker); + } + + return pointers.get(id); } @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 final int pointerCount = me.getPointerCount(); - final int action = me.getAction(); + final int action = me.getActionMasked(); final long eventTime = me.getEventTime(); - if (pointerCount > 1 && mOldPointerCount > 1) { - // Don't do anything when 2 or more pointers are down and moving. - return true; - } - // Track the last few movements to look for spurious swipes. mSwipeTracker.addMovement(me); - // Ignore all motion events until a DOWN. - if (mAbortKey - && action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_CANCEL) { - return true; - } - - if (mGestureDetector.onTouchEvent(me)) { - showPreview(NOT_A_KEY); + // We must disable gesture detector while mini-keyboard is on the screen. + if (mMiniKeyboard == null && mGestureDetector.onTouchEvent(me)) { + dismissKeyPreview(); mHandler.cancelKeyTimers(); return true; } // Needs to be called after the gesture detector gets a turn, as it may have // displayed the mini keyboard - if (mMiniKeyboardOnScreen && action != MotionEvent.ACTION_CANCEL) { + if (mMiniKeyboard != null) { + MotionEvent translated = generateMiniKeyboardMotionEvent(action, (int)me.getX(), + (int)me.getY(), eventTime); + mMiniKeyboard.onTouchEvent(translated); + translated.recycle(); return true; } @@ -1268,145 +1138,58 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener // Up event will pass through. } - int touchX = getTouchX(me.getX()); - int touchY = getTouchY(me.getY()); - if (pointerCount != mOldPointerCount) { - if (pointerCount == 1) { - // Send a down event for the latest pointer - onDownEvent(touchX, touchY, eventTime); - // If it's an up action, then deliver the up as well. - if (action == MotionEvent.ACTION_UP) { - onUpEvent(touchX, touchY, eventTime); - } - } else { - // Send an up event for the last pointer - onUpEvent(mOldPointerX, mOldPointerY, eventTime); + if (action == MotionEvent.ACTION_MOVE) { + for (int index = 0; index < pointerCount; index++) { + int x = (int)me.getX(index); + int y = (int)me.getY(index); + int id = me.getPointerId(index); + PointerTracker tracker = getPointerTracker(id); + tracker.onMoveEvent(x, y, eventTime); } - mOldPointerCount = pointerCount; - return true; } else { - if (pointerCount == 1) { - onModifiedTouchEvent(action, touchX, touchY, eventTime); - mOldPointerX = touchX; - mOldPointerY = touchY; - return true; - } - } - - return false; - } - - private void onModifiedTouchEvent(int action, int touchX, int touchY, long eventTime) { - switch (action) { + int index = me.getActionIndex(); + int x = (int)me.getX(index); + int y = (int)me.getY(index); + int id = me.getPointerId(index); + PointerTracker tracker = getPointerTracker(id); + switch (action) { case MotionEvent.ACTION_DOWN: - onDownEvent(touchX, touchY, eventTime); - break; - case MotionEvent.ACTION_MOVE: - onMoveEvent(touchX, touchY, eventTime); + case MotionEvent.ACTION_POINTER_DOWN: + onDownEvent(tracker, x, y, eventTime); break; case MotionEvent.ACTION_UP: - onUpEvent(touchX, touchY, eventTime); + case MotionEvent.ACTION_POINTER_UP: + onUpEvent(tracker, x, y, eventTime); break; case MotionEvent.ACTION_CANCEL: - onCancelEvent(touchX, touchY, eventTime); + onCancelEvent(tracker, x, y, eventTime); break; - } - } - - private void onDownEvent(int touchX, int touchY, long eventTime) { - int keyIndex = mProximityKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null); - mAbortKey = false; - mCurrentKey = keyIndex; - mStartX = touchX; - mStartY = touchY; - mDebouncer.startMoveDebouncing(touchX, touchY); - mDebouncer.startTimeDebouncing(eventTime); - checkMultiTap(eventTime, keyIndex); - mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ? mKeys[keyIndex].codes[0] : 0); - if (keyIndex >= 0 && mKeys[keyIndex].repeatable) { - repeatKey(keyIndex); - mHandler.startKeyRepeatTimer(REPEAT_START_DELAY, keyIndex); - // Delivering the key could have caused an abort - if (mAbortKey) { - mHandler.cancelKeyRepeatTimer(); - return; } } - if (keyIndex != NOT_A_KEY) { - mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT); - } - showPreview(keyIndex); - mDebouncer.updateMoveDebouncing(touchX, touchY); + + return true; } - private void onMoveEvent(int touchX, int touchY, long eventTime) { - int keyIndex = mProximityKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null); - if (keyIndex != NOT_A_KEY) { - if (mCurrentKey == NOT_A_KEY) { - mDebouncer.updateTimeDebouncing(eventTime); - mCurrentKey = keyIndex; - mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT); - } else if (mDebouncer.isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) { - mDebouncer.updateTimeDebouncing(eventTime); - } else { - resetMultiTap(); - mDebouncer.resetTimeDebouncing(eventTime, mCurrentKey); - mDebouncer.resetMoveDebouncing(); - mCurrentKey = keyIndex; - mHandler.startLongPressTimer(keyIndex, LONGPRESS_TIMEOUT); - } - } else { - mHandler.cancelLongPressTimer(); - } - /* - * While time debouncing is in effect, mCurrentKey holds the new key and mDebouncer - * holds the last key. At ACTION_UP event if time debouncing will be in effect - * eventually, the last key should be sent as the result. In such case mCurrentKey - * should not be showed as popup preview. - */ - showPreview(mDebouncer.isMinorTimeBounce() ? mDebouncer.getLastKey() : mCurrentKey); - mDebouncer.updateMoveDebouncing(touchX, touchY); + private void onDownEvent(PointerTracker tracker, int x, int y, long eventTime) { + tracker.onDownEvent(x, y, eventTime); + mPointerQueue.add(tracker); } - private void onUpEvent(int touchX, int touchY, long eventTime) { - int keyIndex = mProximityKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null); - boolean wasInKeyRepeat = mHandler.isInKeyRepeat(); - mHandler.cancelKeyTimers(); - mHandler.cancelPopupPreview(); - if (mDebouncer.isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) { - mDebouncer.updateTimeDebouncing(eventTime); + private void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) { + int index = mPointerQueue.lastIndexOf(tracker); + if (index >= 0) { + mPointerQueue.releasePointersOlderThan(tracker, eventTime); } else { - resetMultiTap(); - mDebouncer.resetTimeDebouncing(eventTime, mCurrentKey); - mCurrentKey = keyIndex; - } - if (mDebouncer.isMinorTimeBounce()) { - mCurrentKey = mDebouncer.getLastKey(); - touchX = mDebouncer.getLastCodeX(); - touchY = mDebouncer.getLastCodeY(); - } - showPreview(NOT_A_KEY); - // If we're not on a repeating key (which sends on a DOWN event) - if (!wasInKeyRepeat && !mMiniKeyboardOnScreen && !mAbortKey) { - detectAndSendKey(mCurrentKey, touchX, touchY, eventTime); + Log.w(TAG, "onUpEvent: corresponding down event not found for pointer " + + tracker.mPointerId); } - invalidateKey(keyIndex); + tracker.onUpEvent(x, y, eventTime); + mPointerQueue.remove(tracker); } - private void onCancelEvent(int touchX, int touchY, long eventTime) { - mHandler.cancelKeyTimers(); - mHandler.cancelPopupPreview(); - dismissPopupKeyboard(); - mAbortKey = true; - showPreview(NOT_A_KEY); - invalidateKey(mCurrentKey); - } - - private void repeatKey(int keyIndex) { - Key key = mKeys[keyIndex]; - // While key is repeating, because there is no need to handle multi-tap key, we can pass - // -1 as eventTime argument. - detectAndSendKey(keyIndex, key.x, key.y, -1); + private void onCancelEvent(PointerTracker tracker, int x, int y, long eventTime) { + tracker.onCancelEvent(x, y, eventTime); + mPointerQueue.remove(tracker); } protected void swipeRight() { @@ -1444,44 +1227,20 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener } private void dismissPopupKeyboard() { - if (mPopupKeyboard.isShowing()) { - mPopupKeyboard.dismiss(); - mMiniKeyboardOnScreen = false; + if (mMiniKeyboardPopup.isShowing()) { + mMiniKeyboardPopup.dismiss(); + mMiniKeyboard = null; + mMiniKeyboardOriginX = 0; + mMiniKeyboardOriginY = 0; invalidateAllKeys(); } } public boolean handleBack() { - if (mPopupKeyboard.isShowing()) { + if (mMiniKeyboardPopup.isShowing()) { dismissPopupKeyboard(); return true; } return false; } - - private void resetMultiTap() { - mLastSentIndex = NOT_A_KEY; - mTapCount = 0; - mLastTapTime = -1; - mInMultiTap = false; - } - - private void checkMultiTap(long eventTime, int keyIndex) { - if (keyIndex == NOT_A_KEY) return; - Key key = mKeys[keyIndex]; - if (key.codes.length > 1) { - mInMultiTap = true; - if (eventTime < mLastTapTime + MULTITAP_INTERVAL - && keyIndex == mLastSentIndex) { - mTapCount = (mTapCount + 1) % key.codes.length; - return; - } else { - mTapCount = -1; - return; - } - } - if (eventTime > mLastTapTime + MULTITAP_INTERVAL || keyIndex != mLastSentIndex) { - resetMultiTap(); - } - } } |