Merge remote-tracking branch 'goog/master' into merge

main
satok 2011-04-19 23:26:53 +09:00
commit 95f2fb6b92
10 changed files with 333 additions and 259 deletions

View File

@ -35,29 +35,26 @@
latin:keyEdgeFlags="left" /> latin:keyEdgeFlags="left" />
<include <include
latin:keyboardLayout="@xml/kbd_qwerty_f1" /> latin:keyboardLayout="@xml/kbd_qwerty_f1" />
<Key
latin:keyStyle="spaceKeyStyle"
latin:keyWidth="40%p" />
<switch> <switch>
<case <case
latin:mode="web" latin:mode="web"
> >
<Key
latin:keyStyle="spaceKeyStyle"
latin:keyWidth="20%p" />
<Key <Key
latin:keyStyle="tabKeyStyle" latin:keyStyle="tabKeyStyle"
latin:keyWidth="20%p" /> latin:keyWidth="10%p" />
</case> </case>
<default> <default>
<Key
latin:keyStyle="spaceKeyStyle"
latin:keyWidth="40%p" />
</default>
</switch>
<Key <Key
latin:keyLabel="." latin:keyLabel="."
latin:keyHintIcon="@drawable/hint_popup" latin:keyHintIcon="@drawable/hint_popup"
latin:popupCharacters="@string/alternates_for_punctuation" latin:popupCharacters="@string/alternates_for_punctuation"
latin:maxPopupKeyboardColumn="7" latin:maxPopupKeyboardColumn="7"
latin:keyStyle="functionalKeyStyle" /> latin:keyStyle="functionalKeyStyle" />
</default>
</switch>
<switch> <switch>
<case <case
latin:mode="im" latin:mode="im"

View File

@ -153,7 +153,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
makeSymbolsKeyboardIds(id.mMode, attribute); makeSymbolsKeyboardIds(id.mMode, attribute);
mCurrentId = id; mCurrentId = id;
mInputView.setPreviewEnabled(mInputMethodService.getPopupOn()); mInputView.setKeyPreviewEnabled(mInputMethodService.getPopupOn());
setKeyboard(getKeyboard(id)); setKeyboard(getKeyboard(id));
} }

View File

@ -106,26 +106,24 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
// Main keyboard // Main keyboard
private Keyboard mKeyboard; private Keyboard mKeyboard;
// Key preview popup // Key preview
private boolean mInForeground; private boolean mInForeground;
private TextView mPreviewText; private TextView mPreviewText;
private int mPreviewTextSizeLarge; private int mPreviewTextSizeLarge;
private final int[] mOffsetInWindow = new int[2]; private final int[] mOffsetInWindow = new int[2];
private int mOldPreviewKeyIndex = KeyDetector.NOT_A_KEY; private boolean mShowKeyPreview = true;
private boolean mShowPreview = true; private int mKeyPreviewDisplayedY;
private int mPopupPreviewOffsetX;
private int mPopupPreviewOffsetY;
private int mPopupPreviewDisplayedY;
private final int mDelayBeforePreview; private final int mDelayBeforePreview;
private final int mDelayAfterPreview; private final int mDelayAfterPreview;
private ViewGroup mPreviewPlacer;
// Popup mini keyboard // Mini keyboard
private PopupWindow mMiniKeyboardPopup; private PopupWindow mMiniKeyboardWindow;
private KeyboardView mMiniKeyboardView; private KeyboardView mMiniKeyboardView;
private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>(); private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>();
private int mMiniKeyboardOriginX; private int mMiniKeyboardOriginX;
private int mMiniKeyboardOriginY; private int mMiniKeyboardOriginY;
private long mMiniKeyboardPopupTime; private long mMiniKeyboardDisplayedTime;
private int[] mWindowOffset; private int[] mWindowOffset;
private final float mMiniKeyboardSlideAllowance; private final float mMiniKeyboardSlideAllowance;
private int mMiniKeyboardTrackerId; private int mMiniKeyboardTrackerId;
@ -185,8 +183,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
private final UIHandler mHandler = new UIHandler(); private final UIHandler mHandler = new UIHandler();
class UIHandler extends Handler { class UIHandler extends Handler {
private static final int MSG_POPUP_PREVIEW = 1; private static final int MSG_SHOW_KEY_PREVIEW = 1;
private static final int MSG_DISMISS_PREVIEW = 2; private static final int MSG_DISMISS_KEY_PREVIEW = 2;
private static final int MSG_REPEAT_KEY = 3; private static final int MSG_REPEAT_KEY = 3;
private static final int MSG_LONGPRESS_KEY = 4; private static final int MSG_LONGPRESS_KEY = 4;
private static final int MSG_LONGPRESS_SHIFT_KEY = 5; private static final int MSG_LONGPRESS_SHIFT_KEY = 5;
@ -196,10 +194,10 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
switch (msg.what) { switch (msg.what) {
case MSG_POPUP_PREVIEW: case MSG_SHOW_KEY_PREVIEW:
showKey(msg.arg1, (PointerTracker)msg.obj); showKey(msg.arg1, (PointerTracker)msg.obj);
break; break;
case MSG_DISMISS_PREVIEW: case MSG_DISMISS_KEY_PREVIEW:
mPreviewText.setVisibility(View.INVISIBLE); mPreviewText.setVisibility(View.INVISIBLE);
break; break;
case MSG_REPEAT_KEY: { case MSG_REPEAT_KEY: {
@ -210,7 +208,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
} }
case MSG_LONGPRESS_KEY: { case MSG_LONGPRESS_KEY: {
final PointerTracker tracker = (PointerTracker)msg.obj; final PointerTracker tracker = (PointerTracker)msg.obj;
openPopupIfRequired(msg.arg1, tracker); openMiniKeyboardIfRequired(msg.arg1, tracker);
break; break;
} }
case MSG_LONGPRESS_SHIFT_KEY: { case MSG_LONGPRESS_SHIFT_KEY: {
@ -221,26 +219,35 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
} }
} }
public void popupPreview(long delay, int keyIndex, PointerTracker tracker) { public void showKeyPreview(long delay, int keyIndex, PointerTracker tracker) {
removeMessages(MSG_POPUP_PREVIEW); removeMessages(MSG_SHOW_KEY_PREVIEW);
if (mPreviewText.getVisibility() == VISIBLE || delay == 0) { if (mPreviewText.getVisibility() == VISIBLE || delay == 0) {
// Show right away, if it's already visible and finger is moving around // Show right away, if it's already visible and finger is moving around
showKey(keyIndex, tracker); showKey(keyIndex, tracker);
} else { } else {
sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker), delay); sendMessageDelayed(
obtainMessage(MSG_SHOW_KEY_PREVIEW, keyIndex, 0, tracker), delay);
} }
} }
public void cancelPopupPreview() { public void cancelShowKeyPreview(PointerTracker tracker) {
removeMessages(MSG_POPUP_PREVIEW); removeMessages(MSG_SHOW_KEY_PREVIEW, tracker);
} }
public void dismissPreview(long delay) { public void cancelAllShowKeyPreviews() {
sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay); removeMessages(MSG_SHOW_KEY_PREVIEW);
} }
public void cancelDismissPreview() { public void dismissKeyPreview(long delay, PointerTracker tracker) {
removeMessages(MSG_DISMISS_PREVIEW); sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay);
}
public void cancelDismissKeyPreview(PointerTracker tracker) {
removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker);
}
public void cancelAllDismissKeyPreviews() {
removeMessages(MSG_DISMISS_KEY_PREVIEW);
} }
public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) { public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {
@ -282,8 +289,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
public void cancelAllMessages() { public void cancelAllMessages() {
cancelKeyTimers(); cancelKeyTimers();
cancelPopupPreview(); cancelAllShowKeyPreviews();
cancelDismissPreview(); cancelAllDismissKeyPreviews();
} }
} }
@ -363,18 +370,18 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null); mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null);
mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large); mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large);
} else { } else {
mShowPreview = false; mShowKeyPreview = false;
} }
mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview); mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview);
mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview); mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview);
mKeyLabelHorizontalPadding = (int)res.getDimension( mKeyLabelHorizontalPadding = (int)res.getDimension(
R.dimen.key_label_horizontal_alignment_padding); R.dimen.key_label_horizontal_alignment_padding);
mMiniKeyboardPopup = new PopupWindow(context); mMiniKeyboardWindow = new PopupWindow(context);
mMiniKeyboardPopup.setBackgroundDrawable(null); mMiniKeyboardWindow.setBackgroundDrawable(null);
mMiniKeyboardPopup.setAnimationStyle(R.style.MiniKeyboardAnimation); mMiniKeyboardWindow.setAnimationStyle(R.style.MiniKeyboardAnimation);
// Allow popup window to be drawn off the screen. // Allow popup window to be drawn off the screen.
mMiniKeyboardPopup.setClippingEnabled(false); mMiniKeyboardWindow.setClippingEnabled(false);
mPaint = new Paint(); mPaint = new Paint();
mPaint.setAntiAlias(true); mPaint.setAntiAlias(true);
@ -484,11 +491,11 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
*/ */
public void setKeyboard(Keyboard keyboard) { public void setKeyboard(Keyboard keyboard) {
if (mKeyboard != null) { if (mKeyboard != null) {
dismissKeyPreview(); dismissAllKeyPreviews();
} }
// Remove any pending messages, except dismissing preview // Remove any pending messages, except dismissing preview
mHandler.cancelKeyTimers(); mHandler.cancelKeyTimers();
mHandler.cancelPopupPreview(); mHandler.cancelAllShowKeyPreviews();
mKeyboard = keyboard; mKeyboard = keyboard;
LatinImeLogger.onSetKeyboard(keyboard); LatinImeLogger.onSetKeyboard(keyboard);
mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
@ -544,33 +551,28 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
} }
/** /**
* Enables or disables the key feedback popup. This is a popup that shows a magnified * Enables or disables the key feedback preview. This is a preview that shows a magnified
* version of the depressed key. By default the preview is enabled. * version of the depressed key. By default the preview is enabled.
* @param previewEnabled whether or not to enable the key feedback popup * @param previewEnabled whether or not to enable the key feedback preview
* @see #isPreviewEnabled() * @see #isKeyPreviewEnabled()
*/ */
public void setPreviewEnabled(boolean previewEnabled) { public void setKeyPreviewEnabled(boolean previewEnabled) {
mShowPreview = previewEnabled; mShowKeyPreview = previewEnabled;
} }
/** /**
* Returns the enabled state of the key feedback popup. * Returns the enabled state of the key feedback preview
* @return whether or not the key feedback popup is enabled * @return whether or not the key feedback preview is enabled
* @see #setPreviewEnabled(boolean) * @see #setKeyPreviewEnabled(boolean)
*/ */
public boolean isPreviewEnabled() { public boolean isKeyPreviewEnabled() {
return mShowPreview; return mShowKeyPreview;
} }
public int getColorScheme() { public int getColorScheme() {
return mColorScheme; return mColorScheme;
} }
public void setPopupOffset(int x, int y) {
mPopupPreviewOffsetX = x;
mPopupPreviewOffsetY = y;
}
/** /**
* When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key
* codes for adjacent keys. When disabled, only the primary key code will be * codes for adjacent keys. When disabled, only the primary key code will be
@ -878,47 +880,62 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
} }
// TODO: clean up this method. // TODO: clean up this method.
private void dismissKeyPreview() { private void dismissAllKeyPreviews() {
for (PointerTracker tracker : mPointerTrackers) for (PointerTracker tracker : mPointerTrackers) {
tracker.releaseKey(); tracker.setReleasedKeyGraphics();
showPreview(KeyDetector.NOT_A_KEY, null); dismissKeyPreview(tracker);
}
} }
@Override @Override
public void showPreview(int keyIndex, PointerTracker tracker) { public void showKeyPreview(int keyIndex, PointerTracker tracker) {
int oldKeyIndex = mOldPreviewKeyIndex; if (mShowKeyPreview || mKeyboard.needSpacebarPreview(keyIndex)) {
mOldPreviewKeyIndex = keyIndex; mHandler.showKeyPreview(mDelayBeforePreview, keyIndex, tracker);
if ((mShowPreview && oldKeyIndex != keyIndex) || mKeyboard.needSpacebarPreview(keyIndex)) {
if (keyIndex == KeyDetector.NOT_A_KEY) {
mHandler.cancelPopupPreview();
mHandler.dismissPreview(mDelayAfterPreview);
} else if (tracker != null) {
mHandler.popupPreview(mDelayBeforePreview, keyIndex, tracker);
} }
} }
@Override
public void dismissKeyPreview(PointerTracker tracker) {
if (mShowKeyPreview) {
mHandler.cancelShowKeyPreview(tracker);
mHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
}
}
private void addKeyPreview(TextView keyPreview) {
ViewGroup placer = mPreviewPlacer;
if (placer == null) {
final FrameLayout screenContent = (FrameLayout) getRootView().findViewById(
android.R.id.content);
if (android.os.Build.VERSION.SDK_INT >= /* HONEYCOMB */11) {
placer = screenContent;
} else {
// Insert LinearLayout to be able to setMargin because pre-Honeycomb FrameLayout
// could not handle setMargin properly.
placer = new LinearLayout(getContext());
screenContent.addView(placer);
}
mPreviewPlacer = placer;
}
if (placer instanceof FrameLayout) {
placer.addView(keyPreview, new FrameLayout.LayoutParams(0, 0));
} else {
placer.addView(keyPreview, new LinearLayout.LayoutParams(0, 0));
}
} }
// TODO: Introduce minimum duration for displaying key previews // TODO: Introduce minimum duration for displaying key previews
// TODO: Display up to two key previews when the user presses two keys at the same time // TODO: Display up to two key previews when the user presses two keys at the same time
private void showKey(final int keyIndex, PointerTracker tracker) { private void showKey(final int keyIndex, PointerTracker tracker) {
// If the preview popup has no parent view yet, add it to the screen FrameLayout. // If the key preview has no parent view yet, add it to the ViewGroup which can place
// key preview absolutely in SoftInputWindow.
if (mPreviewText.getParent() == null) { if (mPreviewText.getParent() == null) {
final FrameLayout screenContent = (FrameLayout) getRootView() addKeyPreview(mPreviewText);
.findViewById(android.R.id.content);
if (android.os.Build.VERSION.SDK_INT >= /* HONEYCOMB */ 11) {
screenContent.addView(mPreviewText, new FrameLayout.LayoutParams(0, 0));
} else {
// Insert LinearLayout to be able to setMargin because pre-Honeycomb FrameLayout
// could not handle setMargin properly.
final LinearLayout placer = new LinearLayout(getContext());
screenContent.addView(placer);
placer.addView(mPreviewText, new LinearLayout.LayoutParams(0, 0));
}
} }
final Key key = tracker.getKey(keyIndex); final Key key = tracker.getKey(keyIndex);
// If keyIndex is invalid or IME is already closed, we must not show key preview. // If keyIndex is invalid or IME is already closed, we must not show key preview.
// Trying to show preview PopupWindow while root window is closed causes // Trying to show key preview while root window is closed causes
// WindowManager.BadTokenException. // WindowManager.BadTokenException.
if (key == null || !mInForeground) if (key == null || !mInForeground)
return; return;
@ -942,37 +959,35 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
previewIcon != null ? previewIcon : key.getIcon()); previewIcon != null ? previewIcon : key.getIcon());
mPreviewText.setText(null); mPreviewText.setText(null);
} }
mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), keyDrawWidth
+ mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
final int popupHeight = mPreviewHeight;
final ViewGroup.LayoutParams lp = mPreviewText.getLayoutParams();
lp.width = popupWidth;
lp.height = popupHeight;
int popupPreviewX = keyDrawX - (popupWidth - keyDrawWidth) / 2;
int popupPreviewY = key.mY - popupHeight + mPreviewOffset;
mHandler.cancelDismissPreview();
getLocationInWindow(mOffsetInWindow);
mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero
mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero
// Set the preview background state // Set the preview background state
mPreviewText.getBackground().setState( mPreviewText.getBackground().setState(
key.mPopupCharacters != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); key.mPopupCharacters != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
popupPreviewX += mOffsetInWindow[0];
popupPreviewY += mOffsetInWindow[1]; mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int previewWidth = Math.max(mPreviewText.getMeasuredWidth(), keyDrawWidth
+ mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
final int previewHeight = mPreviewHeight;
final ViewGroup.LayoutParams lp = mPreviewText.getLayoutParams();
lp.width = previewWidth;
lp.height = previewHeight;
int previewX = keyDrawX - (previewWidth - keyDrawWidth) / 2;
int previewY = key.mY - previewHeight + mPreviewOffset;
mHandler.cancelAllDismissKeyPreviews();
getLocationInWindow(mOffsetInWindow);
previewX += mOffsetInWindow[0];
previewY += mOffsetInWindow[1];
// Place the key preview. // Place the key preview.
// TODO: Adjust position of key previews which touch screen edges // TODO: Adjust position of key previews which touch screen edges
if (lp instanceof ViewGroup.MarginLayoutParams) { if (lp instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp; ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp;
mlp.setMargins(popupPreviewX, popupPreviewY, 0, 0); mlp.setMargins(previewX, previewY, 0, 0);
} }
// Record popup preview position to display mini-keyboard later at the same position // Record key preview position to display mini-keyboard later at the same position
mPopupPreviewDisplayedY = popupPreviewY; mKeyPreviewDisplayedY = previewY;
mPreviewText.setVisibility(VISIBLE); mPreviewText.setVisibility(VISIBLE);
} }
@ -1007,29 +1022,26 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
invalidate(mInvalidatedKeyRect); invalidate(mInvalidatedKeyRect);
} }
private boolean openPopupIfRequired(int keyIndex, PointerTracker tracker) { private boolean openMiniKeyboardIfRequired(int keyIndex, PointerTracker tracker) {
// Check if we have a popup layout specified first. // Check if we have a popup layout specified first.
if (mPopupLayout == 0) { if (mPopupLayout == 0) {
return false; return false;
} }
Key popupKey = tracker.getKey(keyIndex); Key parentKey = tracker.getKey(keyIndex);
if (popupKey == null) if (parentKey == null)
return false; return false;
boolean result = onLongPress(popupKey, tracker); boolean result = onLongPress(parentKey, tracker);
if (result) { if (result) {
dismissKeyPreview(); dismissAllKeyPreviews();
mMiniKeyboardTrackerId = tracker.mPointerId; mMiniKeyboardTrackerId = tracker.mPointerId;
// Mark this tracker "already processed" and remove it from the pointer queue tracker.onLongPressed(mPointerQueue);
tracker.setAlreadyProcessed();
mPointerQueue.remove(tracker);
} }
return result; return result;
} }
private void onLongPressShiftKey(PointerTracker tracker) { private void onLongPressShiftKey(PointerTracker tracker) {
tracker.setAlreadyProcessed(); tracker.onLongPressed(mPointerQueue);
mPointerQueue.remove(tracker);
mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0); mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
} }
@ -1040,7 +1052,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0); mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
} }
private View inflateMiniKeyboardContainer(Key popupKey) { private View inflateMiniKeyboardContainer(Key parentKey) {
final View container = LayoutInflater.from(getContext()).inflate(mPopupLayout, null); final View container = LayoutInflater.from(getContext()).inflate(mPopupLayout, null);
if (container == null) if (container == null)
throw new NullPointerException(); throw new NullPointerException();
@ -1051,19 +1063,19 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
@Override @Override
public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) { public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
mKeyboardActionListener.onCodeInput(primaryCode, keyCodes, x, y); mKeyboardActionListener.onCodeInput(primaryCode, keyCodes, x, y);
dismissPopupKeyboard(); dismissMiniKeyboard();
} }
@Override @Override
public void onTextInput(CharSequence text) { public void onTextInput(CharSequence text) {
mKeyboardActionListener.onTextInput(text); mKeyboardActionListener.onTextInput(text);
dismissPopupKeyboard(); dismissMiniKeyboard();
} }
@Override @Override
public void onCancelInput() { public void onCancelInput() {
mKeyboardActionListener.onCancelInput(); mKeyboardActionListener.onCancelInput();
dismissPopupKeyboard(); dismissMiniKeyboard();
} }
@Override @Override
@ -1085,7 +1097,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
miniKeyboardView.mGestureDetector = null; miniKeyboardView.mGestureDetector = null;
final Keyboard keyboard = new MiniKeyboardBuilder(this, mKeyboard.getPopupKeyboardResId(), final Keyboard keyboard = new MiniKeyboardBuilder(this, mKeyboard.getPopupKeyboardResId(),
popupKey).build(); parentKey).build();
miniKeyboardView.setKeyboard(keyboard); miniKeyboardView.setKeyboard(keyboard);
container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
@ -1108,20 +1120,20 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
} }
/** /**
* Called when a key is long pressed. By default this will open any popup keyboard associated * Called when a key is long pressed. By default this will open any mini keyboard associated
* with this key through the attributes popupLayout and popupCharacters. * with this key through the attributes popupLayout and popupCharacters.
* @param popupKey the key that was long pressed * @param parentKey the key that was long pressed
* @return true if the long press is handled, false otherwise. Subclasses should call the * @return true if the long press is handled, false otherwise. Subclasses should call the
* method on the base class if the subclass doesn't wish to handle the call. * method on the base class if the subclass doesn't wish to handle the call.
*/ */
protected boolean onLongPress(Key popupKey, PointerTracker tracker) { protected boolean onLongPress(Key parentKey, PointerTracker tracker) {
if (popupKey.mPopupCharacters == null) if (parentKey.mPopupCharacters == null)
return false; return false;
View container = mMiniKeyboardCache.get(popupKey); View container = mMiniKeyboardCache.get(parentKey);
if (container == null) { if (container == null) {
container = inflateMiniKeyboardContainer(popupKey); container = inflateMiniKeyboardContainer(parentKey);
mMiniKeyboardCache.put(popupKey, container); mMiniKeyboardCache.put(parentKey, container);
} }
mMiniKeyboardView = (KeyboardView)container.findViewById(R.id.KeyboardView); mMiniKeyboardView = (KeyboardView)container.findViewById(R.id.KeyboardView);
final MiniKeyboard miniKeyboard = (MiniKeyboard)mMiniKeyboardView.getKeyboard(); final MiniKeyboard miniKeyboard = (MiniKeyboard)mMiniKeyboardView.getKeyboard();
@ -1131,36 +1143,35 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
getLocationInWindow(mWindowOffset); getLocationInWindow(mWindowOffset);
} }
final int pointX = (mConfigShowMiniKeyboardAtTouchedPoint) ? tracker.getLastX() final int pointX = (mConfigShowMiniKeyboardAtTouchedPoint) ? tracker.getLastX()
: popupKey.mX + popupKey.mWidth / 2; : parentKey.mX + parentKey.mWidth / 2;
final int popupX = pointX - miniKeyboard.getDefaultCoordX() final int miniKeyboardX = pointX - miniKeyboard.getDefaultCoordX()
- container.getPaddingLeft() - container.getPaddingLeft()
+ getPaddingLeft() + mWindowOffset[0]; + getPaddingLeft() + mWindowOffset[0];
final int popupY = popupKey.mY - mKeyboard.getVerticalGap() final int miniKeyboardY = parentKey.mY - mKeyboard.getVerticalGap()
- (container.getMeasuredHeight() - container.getPaddingBottom()) - (container.getMeasuredHeight() - container.getPaddingBottom())
+ getPaddingTop() + mWindowOffset[1]; + getPaddingTop() + mWindowOffset[1];
final int x = popupX; final int x = miniKeyboardX;
final int y = mShowPreview && isOneRowKeys(miniKeyboard.getKeys()) final int y = mShowKeyPreview && isOneRowKeys(miniKeyboard.getKeys())
? mPopupPreviewDisplayedY : popupY; ? mKeyPreviewDisplayedY : miniKeyboardY;
mMiniKeyboardOriginX = x + container.getPaddingLeft() - mWindowOffset[0]; mMiniKeyboardOriginX = x + container.getPaddingLeft() - mWindowOffset[0];
mMiniKeyboardOriginY = y + container.getPaddingTop() - mWindowOffset[1]; mMiniKeyboardOriginY = y + container.getPaddingTop() - mWindowOffset[1];
mMiniKeyboardView.setPopupOffset(x, y);
if (miniKeyboard.setShifted( if (miniKeyboard.setShifted(
mKeyboard == null ? false : mKeyboard.isShiftedOrShiftLocked())) { mKeyboard == null ? false : mKeyboard.isShiftedOrShiftLocked())) {
mMiniKeyboardView.invalidateAllKeys(); mMiniKeyboardView.invalidateAllKeys();
} }
// Mini keyboard needs no pop-up key preview displayed. // Mini keyboard needs no pop-up key preview displayed.
mMiniKeyboardView.setPreviewEnabled(false); mMiniKeyboardView.setKeyPreviewEnabled(false);
mMiniKeyboardPopup.setContentView(container); mMiniKeyboardWindow.setContentView(container);
mMiniKeyboardPopup.setWidth(container.getMeasuredWidth()); mMiniKeyboardWindow.setWidth(container.getMeasuredWidth());
mMiniKeyboardPopup.setHeight(container.getMeasuredHeight()); mMiniKeyboardWindow.setHeight(container.getMeasuredHeight());
mMiniKeyboardPopup.showAtLocation(this, Gravity.NO_GRAVITY, x, y); mMiniKeyboardWindow.showAtLocation(this, Gravity.NO_GRAVITY, x, y);
// Inject down event on the key to mini keyboard. // Inject down event on the key to mini keyboard.
final long eventTime = SystemClock.uptimeMillis(); final long eventTime = SystemClock.uptimeMillis();
mMiniKeyboardPopupTime = eventTime; mMiniKeyboardDisplayedTime = eventTime;
final MotionEvent downEvent = generateMiniKeyboardMotionEvent(MotionEvent.ACTION_DOWN, final MotionEvent downEvent = generateMiniKeyboardMotionEvent(MotionEvent.ACTION_DOWN,
pointX, popupKey.mY + popupKey.mHeight / 2, eventTime); pointX, parentKey.mY + parentKey.mHeight / 2, eventTime);
mMiniKeyboardView.onTouchEvent(downEvent); mMiniKeyboardView.onTouchEvent(downEvent);
downEvent.recycle(); downEvent.recycle();
@ -1169,7 +1180,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
} }
private MotionEvent generateMiniKeyboardMotionEvent(int action, int x, int y, long eventTime) { private MotionEvent generateMiniKeyboardMotionEvent(int action, int x, int y, long eventTime) {
return MotionEvent.obtain(mMiniKeyboardPopupTime, eventTime, action, return MotionEvent.obtain(mMiniKeyboardDisplayedTime, eventTime, action,
x - mMiniKeyboardOriginX, y - mMiniKeyboardOriginY, 0); x - mMiniKeyboardOriginX, y - mMiniKeyboardOriginY, 0);
} }
@ -1180,7 +1191,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
// Create pointer trackers until we can get 'id+1'-th tracker, if needed. // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
for (int i = pointers.size(); i <= id; i++) { for (int i = pointers.size(); i <= id; i++) {
final PointerTracker tracker = final PointerTracker tracker =
new PointerTracker(i, mHandler, mKeyDetector, this, getResources()); new PointerTracker(i, this, mHandler, mKeyDetector, this);
if (mKeyboard != null) if (mKeyboard != null)
tracker.setKeyboard(mKeyboard, mKeyHysteresisDistance); tracker.setKeyboard(mKeyboard, mKeyHysteresisDistance);
if (listener != null) if (listener != null)
@ -1226,7 +1237,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
// TODO: Reconcile gesture detection and accessibility features. // TODO: Reconcile gesture detection and accessibility features.
if (mMiniKeyboardView == null && !mIsAccessibilityEnabled if (mMiniKeyboardView == null && !mIsAccessibilityEnabled
&& mGestureDetector != null && mGestureDetector.onTouchEvent(me)) { && mGestureDetector != null && mGestureDetector.onTouchEvent(me)) {
dismissKeyPreview(); dismissAllKeyPreviews();
mHandler.cancelKeyTimers(); mHandler.cancelKeyTimers();
return true; return true;
} }
@ -1323,7 +1334,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
mPreviewText.setVisibility(View.GONE); mPreviewText.setVisibility(View.GONE);
mHandler.cancelAllMessages(); mHandler.cancelAllMessages();
dismissPopupKeyboard(); dismissMiniKeyboard();
mDirtyRect.union(0, 0, getWidth(), getHeight()); mDirtyRect.union(0, 0, getWidth(), getHeight());
mMiniKeyboardCache.clear(); mMiniKeyboardCache.clear();
requestLayout(); requestLayout();
@ -1340,9 +1351,9 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
closing(); closing();
} }
private void dismissPopupKeyboard() { private void dismissMiniKeyboard() {
if (mMiniKeyboardPopup.isShowing()) { if (mMiniKeyboardWindow.isShowing()) {
mMiniKeyboardPopup.dismiss(); mMiniKeyboardWindow.dismiss();
mMiniKeyboardView = null; mMiniKeyboardView = null;
mMiniKeyboardOriginX = 0; mMiniKeyboardOriginX = 0;
mMiniKeyboardOriginY = 0; mMiniKeyboardOriginY = 0;
@ -1351,8 +1362,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
} }
public boolean handleBack() { public boolean handleBack() {
if (mMiniKeyboardPopup.isShowing()) { if (mMiniKeyboardWindow.isShowing()) {
dismissPopupKeyboard(); dismissMiniKeyboard();
return true; return true;
} }
return false; return false;

View File

@ -55,14 +55,14 @@ public class LatinKeyboardView extends KeyboardView {
} }
@Override @Override
public void setPreviewEnabled(boolean previewEnabled) { public void setKeyPreviewEnabled(boolean previewEnabled) {
LatinKeyboard latinKeyboard = getLatinKeyboard(); LatinKeyboard latinKeyboard = getLatinKeyboard();
if (latinKeyboard != null if (latinKeyboard != null
&& (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard())) { && (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard())) {
// Phone and number keyboard never shows popup preview (except language switch). // Phone and number keyboard never shows popup preview (except language switch).
super.setPreviewEnabled(false); super.setKeyPreviewEnabled(false);
} else { } else {
super.setPreviewEnabled(previewEnabled); super.setKeyPreviewEnabled(previewEnabled);
} }
} }

View File

@ -37,7 +37,8 @@ public class PointerTracker {
public interface UIProxy { public interface UIProxy {
public void invalidateKey(Key key); public void invalidateKey(Key key);
public void showPreview(int keyIndex, PointerTracker tracker); public void showKeyPreview(int keyIndex, PointerTracker tracker);
public void dismissKeyPreview(PointerTracker tracker);
public boolean hasDistinctMultitouch(); public boolean hasDistinctMultitouch();
public boolean isAccessibilityEnabled(); public boolean isAccessibilityEnabled();
} }
@ -49,9 +50,7 @@ public class PointerTracker {
private final int mLongPressKeyTimeout; private final int mLongPressKeyTimeout;
private final int mLongPressShiftKeyTimeout; private final int mLongPressShiftKeyTimeout;
// Miscellaneous constants private final KeyboardView mKeyboardView;
private static final int NOT_A_KEY = KeyDetector.NOT_A_KEY;
private final UIProxy mProxy; private final UIProxy mProxy;
private final UIHandler mHandler; private final UIHandler mHandler;
private final KeyDetector mKeyDetector; private final KeyDetector mKeyDetector;
@ -91,9 +90,6 @@ public class PointerTracker {
// ignore modifier key if true // ignore modifier key if true
private boolean mIgnoreModifierKey; private boolean mIgnoreModifierKey;
// pressed key
private int mPreviousKey = NOT_A_KEY;
// Empty {@link KeyboardActionListener} // Empty {@link KeyboardActionListener}
private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() { private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() {
@Override @Override
@ -110,11 +106,12 @@ public class PointerTracker {
public void onSwipeDown() {} public void onSwipeDown() {}
}; };
public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, public PointerTracker(int id, KeyboardView keyboardView, UIHandler handler,
UIProxy proxy, Resources res) { KeyDetector keyDetector, UIProxy proxy) {
if (proxy == null || handler == null || keyDetector == null) if (proxy == null || handler == null || keyDetector == null)
throw new NullPointerException(); throw new NullPointerException();
mPointerId = id; mPointerId = id;
mKeyboardView = keyboardView;
mProxy = proxy; mProxy = proxy;
mHandler = handler; mHandler = handler;
mKeyDetector = keyDetector; mKeyDetector = keyDetector;
@ -122,6 +119,7 @@ public class PointerTracker {
mKeyState = new PointerTrackerKeyState(keyDetector); mKeyState = new PointerTrackerKeyState(keyDetector);
mIsAccessibilityEnabled = proxy.isAccessibilityEnabled(); mIsAccessibilityEnabled = proxy.isAccessibilityEnabled();
mHasDistinctMultitouch = proxy.hasDistinctMultitouch(); mHasDistinctMultitouch = proxy.hasDistinctMultitouch();
final Resources res = mKeyboardView.getResources();
mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled); mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start); mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
mLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout); mLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout);
@ -250,29 +248,24 @@ public class PointerTracker {
return key != null && key.mCode == Keyboard.CODE_SPACE; return key != null && key.mCode == Keyboard.CODE_SPACE;
} }
public void releaseKey() { public void setReleasedKeyGraphics() {
updateKeyGraphics(NOT_A_KEY); setReleasedKeyGraphics(mKeyState.getKeyIndex());
} }
private void updateKeyGraphics(int keyIndex) { private void setReleasedKeyGraphics(int keyIndex) {
int oldKeyIndex = mPreviousKey; final Key key = getKey(keyIndex);
mPreviousKey = keyIndex; if (key != null) {
if (keyIndex != oldKeyIndex) { key.onReleased();
if (isValidKeyIndex(oldKeyIndex)) { mProxy.invalidateKey(key);
final Key oldKey = mKeys.get(oldKeyIndex);
oldKey.onReleased();
mProxy.invalidateKey(oldKey);
}
if (isValidKeyIndex(keyIndex)) {
final Key newKey = mKeys.get(keyIndex);
newKey.onPressed();
mProxy.invalidateKey(newKey);
}
} }
} }
public void setAlreadyProcessed() { private void setPressedKeyGraphics(int keyIndex) {
mKeyAlreadyProcessed = true; final Key key = getKey(keyIndex);
if (key != null && key.mEnabled) {
key.onPressed();
mProxy.invalidateKey(key);
}
} }
private void checkAssertion(PointerTrackerQueue queue) { private void checkAssertion(PointerTrackerQueue queue) {
@ -318,7 +311,7 @@ public class PointerTracker {
if (DEBUG_MODE) if (DEBUG_MODE)
Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT
+ " distance=" + distanceSquared); + " distance=" + distanceSquared);
setAlreadyProcessed(); mKeyAlreadyProcessed = true;
return; return;
} }
} }
@ -346,24 +339,25 @@ public class PointerTracker {
mIsRepeatableKey = false; mIsRepeatableKey = false;
mIsInSlidingKeyInput = false; mIsInSlidingKeyInput = false;
mIgnoreModifierKey = false; mIgnoreModifierKey = false;
final Key key = getKey(keyIndex); if (isValidKeyIndex(keyIndex)) {
if (key != null) {
// This onPress call may have changed keyboard layout. Those cases are detected at // This onPress call may have changed keyboard layout. Those cases are detected at
// {@link #setKeyboard}. In those cases, we should update keyIndex according to the new // {@link #setKeyboard}. In those cases, we should update keyIndex according to the new
// keyboard layout. // keyboard layout.
if (callListenerOnPressAndCheckKeyboardLayoutChange(key, false)) if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), false))
keyIndex = mKeyState.onDownKey(x, y, eventTime); keyIndex = mKeyState.onDownKey(x, y, eventTime);
// Accessibility disables key repeat because users may need to pause on a key to hear // Accessibility disables key repeat because users may need to pause on a key to hear
// its spoken description. // its spoken description.
if (key.mRepeatable && !mIsAccessibilityEnabled) { final Key key = getKey(keyIndex);
if (key != null && key.mRepeatable && !mIsAccessibilityEnabled) {
repeatKey(keyIndex); repeatKey(keyIndex);
mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this); mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this);
mIsRepeatableKey = true; mIsRepeatableKey = true;
} }
startLongPressTimer(keyIndex); startLongPressTimer(keyIndex);
showKeyPreview(keyIndex);
setPressedKeyGraphics(keyIndex);
} }
showKeyPreviewAndUpdateKeyGraphics(keyIndex);
} }
private void startSlidingKeyInput(Key key) { private void startSlidingKeyInput(Key key) {
@ -382,8 +376,9 @@ public class PointerTracker {
final int lastX = keyState.getLastX(); final int lastX = keyState.getLastX();
final int lastY = keyState.getLastY(); final int lastY = keyState.getLastY();
final int oldKeyIndex = keyState.getKeyIndex();
final Key oldKey = getKey(oldKeyIndex);
int keyIndex = keyState.onMoveKey(x, y); int keyIndex = keyState.onMoveKey(x, y);
final Key oldKey = getKey(keyState.getKeyIndex());
if (isValidKeyIndex(keyIndex)) { if (isValidKeyIndex(keyIndex)) {
if (oldKey == null) { if (oldKey == null) {
// The pointer has been slid in to the new key, but the finger was not on any keys. // The pointer has been slid in to the new key, but the finger was not on any keys.
@ -395,10 +390,13 @@ public class PointerTracker {
keyIndex = keyState.onMoveKey(x, y); keyIndex = keyState.onMoveKey(x, y);
keyState.onMoveToNewKey(keyIndex, x, y); keyState.onMoveToNewKey(keyIndex, x, y);
startLongPressTimer(keyIndex); startLongPressTimer(keyIndex);
showKeyPreview(keyIndex);
setPressedKeyGraphics(keyIndex);
} else if (!isMinorMoveBounce(x, y, keyIndex)) { } else if (!isMinorMoveBounce(x, y, keyIndex)) {
// The pointer has been slid in to the new key from the previous key, we must call // The pointer has been slid in to the new key from the previous key, we must call
// onRelease() first to notify that the previous key has been released, then call // onRelease() first to notify that the previous key has been released, then call
// onPress() to notify that the new key is being pressed. // onPress() to notify that the new key is being pressed.
setReleasedKeyGraphics(oldKeyIndex);
callListenerOnRelease(oldKey, oldKey.mCode, true); callListenerOnRelease(oldKey, oldKey.mCode, true);
startSlidingKeyInput(oldKey); startSlidingKeyInput(oldKey);
mHandler.cancelLongPressTimers(); mHandler.cancelLongPressTimers();
@ -410,6 +408,8 @@ public class PointerTracker {
keyIndex = keyState.onMoveKey(x, y); keyIndex = keyState.onMoveKey(x, y);
keyState.onMoveToNewKey(keyIndex, x, y); keyState.onMoveToNewKey(keyIndex, x, y);
startLongPressTimer(keyIndex); startLongPressTimer(keyIndex);
setPressedKeyGraphics(keyIndex);
showKeyPreview(keyIndex);
} else { } else {
// HACK: On some devices, quick successive touches may be translated to sudden // HACK: On some devices, quick successive touches may be translated to sudden
// move by touch panel firmware. This hack detects the case and translates the // move by touch panel firmware. This hack detects the case and translates the
@ -421,11 +421,12 @@ public class PointerTracker {
if (DEBUG_MODE) if (DEBUG_MODE)
Log.w(TAG, String.format("onMoveEvent: sudden move is translated to " Log.w(TAG, String.format("onMoveEvent: sudden move is translated to "
+ "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y)); + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
onUpEventInternal(lastX, lastY, eventTime); onUpEventInternal(lastX, lastY, eventTime, true);
onDownEventInternal(x, y, eventTime); onDownEventInternal(x, y, eventTime);
} else { } else {
setAlreadyProcessed(); mKeyAlreadyProcessed = true;
showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); dismissKeyPreview();
setReleasedKeyGraphics(oldKeyIndex);
} }
return; return;
} }
@ -434,19 +435,19 @@ public class PointerTracker {
if (oldKey != null && !isMinorMoveBounce(x, y, keyIndex)) { if (oldKey != null && !isMinorMoveBounce(x, y, keyIndex)) {
// The pointer has been slid out from the previous key, we must call onRelease() to // The pointer has been slid out from the previous key, we must call onRelease() to
// notify that the previous key has been released. // notify that the previous key has been released.
setReleasedKeyGraphics(oldKeyIndex);
callListenerOnRelease(oldKey, oldKey.mCode, true); callListenerOnRelease(oldKey, oldKey.mCode, true);
startSlidingKeyInput(oldKey); startSlidingKeyInput(oldKey);
mHandler.cancelLongPressTimers(); mHandler.cancelLongPressTimers();
if (mIsAllowedSlidingKeyInput) { if (mIsAllowedSlidingKeyInput) {
keyState.onMoveToNewKey(keyIndex, x ,y); keyState.onMoveToNewKey(keyIndex, x, y);
} else { } else {
setAlreadyProcessed(); mKeyAlreadyProcessed = true;
showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); dismissKeyPreview();
return; return;
} }
} }
} }
showKeyPreviewAndUpdateKeyGraphics(keyState.getKeyIndex());
} }
public void onUpEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { public void onUpEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
@ -464,35 +465,48 @@ public class PointerTracker {
} }
queue.remove(this); queue.remove(this);
} }
onUpEventInternal(x, y, eventTime); onUpEventInternal(x, y, eventTime, true);
} }
public void onUpEventForRelease(int x, int y, long eventTime) { // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
onUpEventInternal(x, y, eventTime); // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
// "virtual" up event.
public void onPhantomUpEvent(int x, int y, long eventTime) {
onUpEventInternal(x, y, eventTime, false);
mKeyAlreadyProcessed = true;
} }
private void onUpEventInternal(int pointX, int pointY, long eventTime) { private void onUpEventInternal(int x, int y, long eventTime,
int x = pointX; boolean updateReleasedKeyGraphics) {
int y = pointY;
mHandler.cancelKeyTimers(); mHandler.cancelKeyTimers();
mHandler.cancelPopupPreview(); mHandler.cancelShowKeyPreview(this);
showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
mIsInSlidingKeyInput = false; mIsInSlidingKeyInput = false;
final PointerTrackerKeyState keyState = mKeyState;
final int keyX, keyY;
if (!isMinorMoveBounce(x, y, keyState.onMoveKey(x, y))) {
keyX = x;
keyY = y;
} else {
// Use previous fixed key coordinates.
keyX = keyState.getKeyX();
keyY = keyState.getKeyY();
}
final int keyIndex = keyState.onUpKey(keyX, keyY, eventTime);
dismissKeyPreview();
if (updateReleasedKeyGraphics)
setReleasedKeyGraphics(keyIndex);
if (mKeyAlreadyProcessed) if (mKeyAlreadyProcessed)
return; return;
final PointerTrackerKeyState keyState = mKeyState;
int keyIndex = keyState.onUpKey(x, y, eventTime);
if (isMinorMoveBounce(x, y, keyIndex)) {
// Use previous fixed key index and coordinates.
keyIndex = keyState.getKeyIndex();
x = keyState.getKeyX();
y = keyState.getKeyY();
}
if (!mIsRepeatableKey) { if (!mIsRepeatableKey) {
detectAndSendKey(keyIndex, x, y); detectAndSendKey(keyIndex, keyX, keyY);
} }
} }
public void onLongPressed(PointerTrackerQueue queue) {
mKeyAlreadyProcessed = true;
queue.remove(this);
}
public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
if (ENABLE_ASSERTION) checkAssertion(queue); if (ENABLE_ASSERTION) checkAssertion(queue);
if (DEBUG_EVENT) if (DEBUG_EVENT)
@ -505,8 +519,9 @@ public class PointerTracker {
private void onCancelEventInternal() { private void onCancelEventInternal() {
mHandler.cancelKeyTimers(); mHandler.cancelKeyTimers();
mHandler.cancelPopupPreview(); mHandler.cancelShowKeyPreview(this);
showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); dismissKeyPreview();
setReleasedKeyGraphics(mKeyState.getKeyIndex());
mIsInSlidingKeyInput = false; mIsInSlidingKeyInput = false;
} }
@ -542,7 +557,7 @@ public class PointerTracker {
} }
} }
private void showKeyPreviewAndUpdateKeyGraphics(int keyIndex) { private void showKeyPreview(int keyIndex) {
final Key key = getKey(keyIndex); final Key key = getKey(keyIndex);
if (key != null && !key.mEnabled) if (key != null && !key.mEnabled)
return; return;
@ -550,12 +565,13 @@ public class PointerTracker {
// supported. On the other hand, if multi-touch is not supported, the modifier key should // supported. On the other hand, if multi-touch is not supported, the modifier key should
// be shown as preview. If accessibility is turned on, the modifier key should be shown as // be shown as preview. If accessibility is turned on, the modifier key should be shown as
// preview. // preview.
if (mHasDistinctMultitouch && isModifier() && !mIsAccessibilityEnabled) { if (mHasDistinctMultitouch && isModifier() && !mIsAccessibilityEnabled)
mProxy.showPreview(NOT_A_KEY, this); return;
} else { mProxy.showKeyPreview(keyIndex, this);
mProxy.showPreview(keyIndex, this);
} }
updateKeyGraphics(keyIndex);
private void dismissKeyPreview() {
mProxy.dismissKeyPreview(this);
} }
private void startLongPressTimer(int keyIndex) { private void startLongPressTimer(int keyIndex) {

View File

@ -92,6 +92,7 @@ package com.android.inputmethod.keyboard;
public int onUpKey(int x, int y, long eventTime) { public int onUpKey(int x, int y, long eventTime) {
mUpTime = eventTime; mUpTime = eventTime;
mKeyIndex = KeyDetector.NOT_A_KEY;
return onMoveKeyInternal(x, y); return onMoveKeyInternal(x, y);
} }
} }

View File

@ -29,14 +29,13 @@ public class PointerTrackerQueue {
if (mQueue.lastIndexOf(tracker) < 0) { if (mQueue.lastIndexOf(tracker) < 0) {
return; return;
} }
LinkedList<PointerTracker> queue = mQueue; final LinkedList<PointerTracker> queue = mQueue;
int oldestPos = 0; int oldestPos = 0;
for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) { for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) {
if (t.isModifier()) { if (t.isModifier()) {
oldestPos++; oldestPos++;
} else { } else {
t.onUpEventForRelease(t.getLastX(), t.getLastY(), eventTime); t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
t.setAlreadyProcessed();
queue.remove(oldestPos); queue.remove(oldestPos);
} }
} }
@ -50,8 +49,7 @@ public class PointerTrackerQueue {
for (PointerTracker t : mQueue) { for (PointerTracker t : mQueue) {
if (t == tracker) if (t == tracker)
continue; continue;
t.onUpEventForRelease(t.getLastX(), t.getLastY(), eventTime); t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
t.setAlreadyProcessed();
} }
mQueue.clear(); mQueue.clear();
if (tracker != null) if (tracker != null)

View File

@ -162,13 +162,20 @@ public class InputLanguageSelection extends PreferenceActivity {
for (int i = 0 ; i < origSize; i++ ) { for (int i = 0 ; i < origSize; i++ ) {
String s = locales[i]; String s = locales[i];
int len = s.length(); int len = s.length();
String language = "";
String country = "";
if (len == 5) { if (len == 5) {
String language = s.substring(0, 2); language = s.substring(0, 2);
String country = s.substring(3, 5); country = s.substring(3, 5);
} else if (len < 5) {
language = s;
}
Locale l = new Locale(language, country); Locale l = new Locale(language, country);
// Exclude languages that are not relevant to LatinIME // Exclude languages that are not relevant to LatinIME
if (arrayContains(BLACKLIST_LANGUAGES, language)) continue; if (arrayContains(BLACKLIST_LANGUAGES, language) || TextUtils.isEmpty(language)) {
continue;
}
if (finalSize == 0) { if (finalSize == 0) {
preprocess[finalSize++] = preprocess[finalSize++] =
@ -195,7 +202,6 @@ public class InputLanguageSelection extends PreferenceActivity {
} }
} }
} }
}
for (int i = 0; i < finalSize ; i++) { for (int i = 0; i < finalSize ; i++) {
uniqueLocales.add(preprocess[i]); uniqueLocales.add(preprocess[i]);
} }

View File

@ -592,7 +592,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
final boolean accessibilityEnabled = mAccessibilityUtils.isAccessibilityEnabled(); final boolean accessibilityEnabled = mAccessibilityUtils.isAccessibilityEnabled();
inputView.setPreviewEnabled(mPopupOn); inputView.setKeyPreviewEnabled(mPopupOn);
inputView.setProximityCorrectionEnabled(true); inputView.setProximityCorrectionEnabled(true);
inputView.setAccessibilityEnabled(accessibilityEnabled); inputView.setAccessibilityEnabled(accessibilityEnabled);
// If we just entered a text field, maybe it has some old text that requires correction // If we just entered a text field, maybe it has some old text that requires correction

View File

@ -417,6 +417,54 @@ inline static void multiplyRate(const int rate, int *freq) {
} }
} }
inline static int calcFreqForSplitTwoWords(
const int typedLetterMultiplier, const int firstWordLength,
const int secondWordLength, const int firstFreq, const int secondFreq) {
if (firstWordLength == 0 || secondWordLength == 0) {
return 0;
}
const int firstDemotionRate = 100 - 100 / (firstWordLength + 1);
int tempFirstFreq = firstFreq;
multiplyRate(firstDemotionRate, &tempFirstFreq);
const int secondDemotionRate = 100 - 100 / (secondWordLength + 1);
int tempSecondFreq = secondFreq;
multiplyRate(secondDemotionRate, &tempSecondFreq);
const int totalLength = firstWordLength + secondWordLength;
// Promote pairFreq with multiplying by 2, because the word length is the same as the typed
// length.
int totalFreq = tempFirstFreq + tempSecondFreq;
// This is a workaround to try offsetting the not-enough-demotion which will be done in
// calcNormalizedScore in Utils.java.
// In calcNormalizedScore the score will be demoted by (1 - 1 / length)
// but we demoted only (1 - 1 / (length + 1)) so we will additionally adjust freq by
// (1 - 1 / length) / (1 - 1 / (length + 1)) = (1 - 1 / (length * length))
const int normalizedScoreNotEnoughDemotionAdjustment = 100 - 100 / (totalLength * totalLength);
multiplyRate(normalizedScoreNotEnoughDemotionAdjustment, &totalFreq);
// At this moment, totalFreq is calculated by the following formula:
// (firstFreq * (1 - 1 / (firstWordLength + 1)) + secondFreq * (1 - 1 / (secondWordLength + 1)))
// * (1 - 1 / totalLength) / (1 - 1 / (totalLength + 1))
for (int i = 0; i < totalLength; ++i) {
totalFreq *= typedLetterMultiplier;
}
// This is another workaround to offset the demotion which will be done in
// calcNormalizedScore in Utils.java.
// In calcNormalizedScore the score will be demoted by (1 - 1 / length) so we have to promote
// the same amount because we already have adjusted the synthetic freq of this "missing or
// mistyped space" suggestion candidate above in this method.
const int normalizedScoreDemotionRateOffset = (100 + 100 / totalLength);
multiplyRate(normalizedScoreDemotionRateOffset, &totalFreq);
multiplyRate(WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE, &totalFreq);
return totalFreq;
}
bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength, bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength,
const int firstWordStartPos, const int firstWordLength, const int secondWordStartPos, const int firstWordStartPos, const int firstWordLength, const int secondWordStartPos,
const int secondWordLength) { const int secondWordLength) {
@ -448,15 +496,12 @@ bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength,
word[i] = mWord[i - firstWordLength - 1]; word[i] = mWord[i - firstWordLength - 1];
} }
// Promote pairFreq with multiplying by 2, because the word length is the same as the typed int pairFreq = calcFreqForSplitTwoWords(
// length. TYPED_LETTER_MULTIPLIER, firstWordLength, secondWordLength, firstFreq, secondFreq);
int pairFreq = firstFreq + secondFreq;
for (int i = 0; i < inputLength; ++i) pairFreq *= TYPED_LETTER_MULTIPLIER;
if (DEBUG_DICT) { if (DEBUG_DICT) {
LOGI("Missing space: %d, %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength, LOGI("Missing space: %d, %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength,
TYPED_LETTER_MULTIPLIER); TYPED_LETTER_MULTIPLIER);
} }
multiplyRate(WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE, &pairFreq);
addWord(word, newWordLength, pairFreq); addWord(word, newWordLength, pairFreq);
return true; return true;
} }