Support sliding pop-up mini-keyboard input
TODO: - Remove close button on mini-keyboard - Dismiss mini-keyboard when finger leave the screen while no key is selected This change also renames some instance variables to have more meaningful name. Bug: 2959169 Change-Id: I9fd79116a647d7be82415c6e9e7cdaf6edcb2bf6main
parent
f0fd506520
commit
07221a4ad1
|
@ -185,17 +185,20 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
private boolean mShowTouchPoints = true;
|
private boolean mShowTouchPoints = true;
|
||||||
private int mPopupPreviewX;
|
private int mPopupPreviewX;
|
||||||
private int mPopupPreviewY;
|
private int mPopupPreviewY;
|
||||||
|
private int mPopupPreviewOffsetX;
|
||||||
|
private int mPopupPreviewOffsetY;
|
||||||
private int mWindowY;
|
private int mWindowY;
|
||||||
|
|
||||||
// Popup mini keyboard
|
// Popup mini keyboard
|
||||||
private PopupWindow mPopupKeyboard;
|
private PopupWindow mMiniKeyboardPopup;
|
||||||
private View mMiniKeyboardContainer;
|
private View mMiniKeyboardContainer;
|
||||||
private LatinKeyboardBaseView mMiniKeyboard;
|
private LatinKeyboardBaseView mMiniKeyboard;
|
||||||
private boolean mMiniKeyboardOnScreen;
|
private boolean mMiniKeyboardOnScreen;
|
||||||
private View mPopupParent;
|
private View mMiniKeyboardParent;
|
||||||
private int mMiniKeyboardOffsetX;
|
|
||||||
private int mMiniKeyboardOffsetY;
|
|
||||||
private Map<Key,View> mMiniKeyboardCache;
|
private Map<Key,View> mMiniKeyboardCache;
|
||||||
|
private int mMiniKeyboardOriginX;
|
||||||
|
private int mMiniKeyboardOriginY;
|
||||||
|
private long mMiniKeyboardPopupTime;
|
||||||
private int[] mWindowOffset;
|
private int[] mWindowOffset;
|
||||||
|
|
||||||
/** Listener for {@link OnKeyboardActionListener}. */
|
/** Listener for {@link OnKeyboardActionListener}. */
|
||||||
|
@ -445,10 +448,10 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
mShowPreview = false;
|
mShowPreview = false;
|
||||||
}
|
}
|
||||||
mPreviewPopup.setTouchable(false);
|
mPreviewPopup.setTouchable(false);
|
||||||
mPopupParent = this;
|
mMiniKeyboardParent = this;
|
||||||
|
|
||||||
mPopupKeyboard = new PopupWindow(context);
|
mMiniKeyboardPopup = new PopupWindow(context);
|
||||||
mPopupKeyboard.setBackgroundDrawable(null);
|
mMiniKeyboardPopup.setBackgroundDrawable(null);
|
||||||
|
|
||||||
mPaint = new Paint();
|
mPaint = new Paint();
|
||||||
mPaint.setAntiAlias(true);
|
mPaint.setAntiAlias(true);
|
||||||
|
@ -615,12 +618,12 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPopupParent(View v) {
|
public void setPopupParent(View v) {
|
||||||
mPopupParent = v;
|
mMiniKeyboardParent = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPopupOffset(int x, int y) {
|
public void setPopupOffset(int x, int y) {
|
||||||
mMiniKeyboardOffsetX = x;
|
mPopupPreviewOffsetX = x;
|
||||||
mMiniKeyboardOffsetY = y;
|
mPopupPreviewOffsetY = y;
|
||||||
if (mPreviewPopup.isShowing()) {
|
if (mPreviewPopup.isShowing()) {
|
||||||
mPreviewPopup.dismiss();
|
mPreviewPopup.dismiss();
|
||||||
}
|
}
|
||||||
|
@ -896,8 +899,8 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
if (mOffsetInWindow == null) {
|
if (mOffsetInWindow == null) {
|
||||||
mOffsetInWindow = new int[2];
|
mOffsetInWindow = new int[2];
|
||||||
getLocationInWindow(mOffsetInWindow);
|
getLocationInWindow(mOffsetInWindow);
|
||||||
mOffsetInWindow[0] += mMiniKeyboardOffsetX; // Offset may be zero
|
mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero
|
||||||
mOffsetInWindow[1] += mMiniKeyboardOffsetY; // Offset may be zero
|
mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero
|
||||||
int[] mWindowLocation = new int[2];
|
int[] mWindowLocation = new int[2];
|
||||||
getLocationOnScreen(mWindowLocation);
|
getLocationOnScreen(mWindowLocation);
|
||||||
mWindowY = mWindowLocation[1];
|
mWindowY = mWindowLocation[1];
|
||||||
|
@ -926,7 +929,7 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
} else {
|
} else {
|
||||||
previewPopup.setWidth(popupWidth);
|
previewPopup.setWidth(popupWidth);
|
||||||
previewPopup.setHeight(popupHeight);
|
previewPopup.setHeight(popupHeight);
|
||||||
previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY,
|
previewPopup.showAtLocation(mMiniKeyboardParent, Gravity.NO_GRAVITY,
|
||||||
mPopupPreviewX, mPopupPreviewY);
|
mPopupPreviewX, mPopupPreviewY);
|
||||||
}
|
}
|
||||||
mPreviewText.setVisibility(VISIBLE);
|
mPreviewText.setVisibility(VISIBLE);
|
||||||
|
@ -974,6 +977,13 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
boolean result = onLongPress(popupKey);
|
boolean result = onLongPress(popupKey);
|
||||||
if (result) {
|
if (result) {
|
||||||
dismissKeyPreview();
|
dismissKeyPreview();
|
||||||
|
|
||||||
|
long eventTime = tracker.getDownTime();
|
||||||
|
mMiniKeyboardPopupTime = eventTime;
|
||||||
|
MotionEvent downEvent = generateMiniKeyboardMotionEvent(MotionEvent.ACTION_DOWN,
|
||||||
|
tracker.getLastX(), tracker.getLastY(), eventTime);
|
||||||
|
mMiniKeyboard.onTouchEvent(downEvent);
|
||||||
|
downEvent.recycle();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1020,7 +1030,7 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
mKeyboardActionListener.onRelease(primaryCode);
|
mKeyboardActionListener.onRelease(primaryCode);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//mInputView.setSuggest(mSuggest);
|
|
||||||
Keyboard keyboard;
|
Keyboard keyboard;
|
||||||
if (popupKey.popupCharacters != null) {
|
if (popupKey.popupCharacters != null) {
|
||||||
keyboard = new Keyboard(getContext(), popupKeyboardId,
|
keyboard = new Keyboard(getContext(), popupKeyboardId,
|
||||||
|
@ -1043,20 +1053,25 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
mWindowOffset = new int[2];
|
mWindowOffset = new int[2];
|
||||||
getLocationInWindow(mWindowOffset);
|
getLocationInWindow(mWindowOffset);
|
||||||
}
|
}
|
||||||
int popupX = popupKey.x + getPaddingLeft();
|
int popupX = popupKey.x + popupKey.width + getPaddingLeft();
|
||||||
int popupY = popupKey.y + getPaddingTop();
|
int popupY = popupKey.y + getPaddingTop();
|
||||||
popupX = popupX + popupKey.width - mMiniKeyboardContainer.getMeasuredWidth();
|
popupX -= mMiniKeyboardContainer.getMeasuredWidth();
|
||||||
popupY = popupY - mMiniKeyboardContainer.getMeasuredHeight();
|
popupY -= mMiniKeyboardContainer.getMeasuredHeight();
|
||||||
final int x = popupX + mMiniKeyboardContainer.getPaddingRight() + mWindowOffset[0];
|
popupX += mWindowOffset[0];
|
||||||
final int y = popupY + mMiniKeyboardContainer.getPaddingBottom() + mWindowOffset[1];
|
popupY += mWindowOffset[1];
|
||||||
mMiniKeyboard.setPopupOffset(x < 0 ? 0 : x, y);
|
final int x = popupX + mMiniKeyboardContainer.getPaddingRight();
|
||||||
|
final int y = popupY + mMiniKeyboardContainer.getPaddingBottom();
|
||||||
|
mMiniKeyboardOriginX = (x < 0 ? 0 : x) + mMiniKeyboardContainer.getPaddingLeft();
|
||||||
|
mMiniKeyboardOriginY = y + mMiniKeyboardContainer.getPaddingTop();
|
||||||
|
mMiniKeyboard.setPopupOffset((x < 0) ? 0 : x, y);
|
||||||
mMiniKeyboard.setShifted(isShifted());
|
mMiniKeyboard.setShifted(isShifted());
|
||||||
mPopupKeyboard.setContentView(mMiniKeyboardContainer);
|
mMiniKeyboardPopup.setContentView(mMiniKeyboardContainer);
|
||||||
mPopupKeyboard.setWidth(mMiniKeyboardContainer.getMeasuredWidth());
|
mMiniKeyboardPopup.setWidth(mMiniKeyboardContainer.getMeasuredWidth());
|
||||||
mPopupKeyboard.setHeight(mMiniKeyboardContainer.getMeasuredHeight());
|
mMiniKeyboardPopup.setHeight(mMiniKeyboardContainer.getMeasuredHeight());
|
||||||
mPopupKeyboard.showAtLocation(this, Gravity.NO_GRAVITY, x, y);
|
mMiniKeyboardPopup.showAtLocation(this, Gravity.NO_GRAVITY, x, y);
|
||||||
mMiniKeyboardOnScreen = true;
|
mMiniKeyboardOnScreen = true;
|
||||||
//mMiniKeyboard.onTouchEvent(getTranslatedEvent(me));
|
|
||||||
|
// TODO: down event?
|
||||||
invalidateAllKeys();
|
invalidateAllKeys();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1068,6 +1083,11 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
return mMiniKeyboardOnScreen;
|
return mMiniKeyboardOnScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MotionEvent generateMiniKeyboardMotionEvent(int action, int x, int y, long eventTime) {
|
||||||
|
return MotionEvent.obtain(mMiniKeyboardPopupTime, eventTime, action,
|
||||||
|
x - mMiniKeyboardOriginX, y - mMiniKeyboardOriginY, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private PointerTracker getPointerTracker(final int id) {
|
private PointerTracker getPointerTracker(final int id) {
|
||||||
final ArrayList<PointerTracker> pointers = mPointerTrackers;
|
final ArrayList<PointerTracker> pointers = mPointerTrackers;
|
||||||
final Key[] keys = mKeys;
|
final Key[] keys = mKeys;
|
||||||
|
@ -1105,7 +1125,11 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
|
|
||||||
// Needs to be called after the gesture detector gets a turn, as it may have
|
// Needs to be called after the gesture detector gets a turn, as it may have
|
||||||
// displayed the mini keyboard
|
// displayed the mini keyboard
|
||||||
if (mMiniKeyboardOnScreen && action != MotionEvent.ACTION_CANCEL) {
|
if (mMiniKeyboardOnScreen) {
|
||||||
|
MotionEvent translated = generateMiniKeyboardMotionEvent(action, (int)me.getX(),
|
||||||
|
(int)me.getY(), eventTime);
|
||||||
|
mMiniKeyboard.onTouchEvent(translated);
|
||||||
|
translated.recycle();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1209,15 +1233,17 @@ public class LatinKeyboardBaseView extends View implements View.OnClickListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dismissPopupKeyboard() {
|
public void dismissPopupKeyboard() {
|
||||||
if (mPopupKeyboard.isShowing()) {
|
if (mMiniKeyboardPopup.isShowing()) {
|
||||||
mPopupKeyboard.dismiss();
|
mMiniKeyboardPopup.dismiss();
|
||||||
mMiniKeyboardOnScreen = false;
|
mMiniKeyboardOnScreen = false;
|
||||||
|
mMiniKeyboardOriginX = 0;
|
||||||
|
mMiniKeyboardOriginY = 0;
|
||||||
invalidateAllKeys();
|
invalidateAllKeys();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean handleBack() {
|
public boolean handleBack() {
|
||||||
if (mPopupKeyboard.isShowing()) {
|
if (mMiniKeyboardPopup.isShowing()) {
|
||||||
dismissPopupKeyboard();
|
dismissPopupKeyboard();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ public class PointerTracker {
|
||||||
private int mCurrentKey = NOT_A_KEY;
|
private int mCurrentKey = NOT_A_KEY;
|
||||||
private int mStartX;
|
private int mStartX;
|
||||||
private int mStartY;
|
private int mStartY;
|
||||||
|
private long mDownTime;
|
||||||
|
|
||||||
// for move de-bouncing
|
// for move de-bouncing
|
||||||
private int mLastCodeX;
|
private int mLastCodeX;
|
||||||
|
@ -144,6 +145,7 @@ public class PointerTracker {
|
||||||
mCurrentKey = keyIndex;
|
mCurrentKey = keyIndex;
|
||||||
mStartX = x;
|
mStartX = x;
|
||||||
mStartY = y;
|
mStartY = y;
|
||||||
|
mDownTime = eventTime;
|
||||||
startMoveDebouncing(x, y);
|
startMoveDebouncing(x, y);
|
||||||
startTimeDebouncing(eventTime);
|
startTimeDebouncing(eventTime);
|
||||||
checkMultiTap(eventTime, keyIndex);
|
checkMultiTap(eventTime, keyIndex);
|
||||||
|
@ -181,7 +183,19 @@ public class PointerTracker {
|
||||||
mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
|
mHandler.startLongPressTimer(LONGPRESS_TIMEOUT, keyIndex, this);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mHandler.cancelLongPressTimer();
|
if (mCurrentKey != NOT_A_KEY) {
|
||||||
|
updateTimeDebouncing(eventTime);
|
||||||
|
mCurrentKey = keyIndex;
|
||||||
|
mHandler.cancelLongPressTimer();
|
||||||
|
} else if (isMinorMoveBounce(x, y, keyIndex, mCurrentKey)) {
|
||||||
|
updateTimeDebouncing(eventTime);
|
||||||
|
} else {
|
||||||
|
resetMultiTap();
|
||||||
|
resetTimeDebouncing(eventTime, mCurrentKey);
|
||||||
|
resetMoveDebouncing();
|
||||||
|
mCurrentKey = keyIndex;
|
||||||
|
mHandler.cancelLongPressTimer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* While time debouncing is in effect, mCurrentKey holds the new key and this tracker
|
* While time debouncing is in effect, mCurrentKey holds the new key and this tracker
|
||||||
|
@ -252,6 +266,10 @@ public class PointerTracker {
|
||||||
return mLastY;
|
return mLastY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getDownTime() {
|
||||||
|
return mDownTime;
|
||||||
|
}
|
||||||
|
|
||||||
// These package scope methods are only for debugging purpose.
|
// These package scope methods are only for debugging purpose.
|
||||||
/* package */ int getStartX() {
|
/* package */ int getStartX() {
|
||||||
return mStartX;
|
return mStartX;
|
||||||
|
|
Loading…
Reference in New Issue