diff --git a/java/res/xml/kbd_qwerty_row4.xml b/java/res/xml/kbd_qwerty_row4.xml index 0db011672..18ff608dd 100644 --- a/java/res/xml/kbd_qwerty_row4.xml +++ b/java/res/xml/kbd_qwerty_row4.xml @@ -35,29 +35,26 @@ latin:keyEdgeFlags="left" /> + - - + latin:keyWidth="10%p" /> + latin:keyLabel="." + latin:keyHintIcon="@drawable/hint_popup" + latin:popupCharacters="@string/alternates_for_punctuation" + latin:maxPopupKeyboardColumn="7" + latin:keyStyle="functionalKeyStyle" /> - mMiniKeyboardCache = new WeakHashMap(); private int mMiniKeyboardOriginX; private int mMiniKeyboardOriginY; - private long mMiniKeyboardPopupTime; + private long mMiniKeyboardDisplayedTime; private int[] mWindowOffset; private final float mMiniKeyboardSlideAllowance; private int mMiniKeyboardTrackerId; @@ -185,8 +183,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { 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_SHOW_KEY_PREVIEW = 1; + private static final int MSG_DISMISS_KEY_PREVIEW = 2; private static final int MSG_REPEAT_KEY = 3; private static final int MSG_LONGPRESS_KEY = 4; private static final int MSG_LONGPRESS_SHIFT_KEY = 5; @@ -196,10 +194,10 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_POPUP_PREVIEW: + case MSG_SHOW_KEY_PREVIEW: showKey(msg.arg1, (PointerTracker)msg.obj); break; - case MSG_DISMISS_PREVIEW: + case MSG_DISMISS_KEY_PREVIEW: mPreviewText.setVisibility(View.INVISIBLE); break; case MSG_REPEAT_KEY: { @@ -210,7 +208,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { } case MSG_LONGPRESS_KEY: { final PointerTracker tracker = (PointerTracker)msg.obj; - openPopupIfRequired(msg.arg1, tracker); + openMiniKeyboardIfRequired(msg.arg1, tracker); break; } 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) { - removeMessages(MSG_POPUP_PREVIEW); + public void showKeyPreview(long delay, int keyIndex, PointerTracker tracker) { + removeMessages(MSG_SHOW_KEY_PREVIEW); if (mPreviewText.getVisibility() == VISIBLE || delay == 0) { // 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); + sendMessageDelayed( + obtainMessage(MSG_SHOW_KEY_PREVIEW, keyIndex, 0, tracker), delay); } } - public void cancelPopupPreview() { - removeMessages(MSG_POPUP_PREVIEW); + public void cancelShowKeyPreview(PointerTracker tracker) { + removeMessages(MSG_SHOW_KEY_PREVIEW, tracker); } - public void dismissPreview(long delay) { - sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay); + public void cancelAllShowKeyPreviews() { + removeMessages(MSG_SHOW_KEY_PREVIEW); } - public void cancelDismissPreview() { - removeMessages(MSG_DISMISS_PREVIEW); + public void dismissKeyPreview(long delay, PointerTracker tracker) { + 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) { @@ -282,8 +289,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { public void cancelAllMessages() { cancelKeyTimers(); - cancelPopupPreview(); - cancelDismissPreview(); + cancelAllShowKeyPreviews(); + cancelAllDismissKeyPreviews(); } } @@ -363,18 +370,18 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null); mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large); } else { - mShowPreview = false; + mShowKeyPreview = false; } mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview); mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview); mKeyLabelHorizontalPadding = (int)res.getDimension( R.dimen.key_label_horizontal_alignment_padding); - mMiniKeyboardPopup = new PopupWindow(context); - mMiniKeyboardPopup.setBackgroundDrawable(null); - mMiniKeyboardPopup.setAnimationStyle(R.style.MiniKeyboardAnimation); + mMiniKeyboardWindow = new PopupWindow(context); + mMiniKeyboardWindow.setBackgroundDrawable(null); + mMiniKeyboardWindow.setAnimationStyle(R.style.MiniKeyboardAnimation); // Allow popup window to be drawn off the screen. - mMiniKeyboardPopup.setClippingEnabled(false); + mMiniKeyboardWindow.setClippingEnabled(false); mPaint = new Paint(); mPaint.setAntiAlias(true); @@ -484,11 +491,11 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { */ public void setKeyboard(Keyboard keyboard) { if (mKeyboard != null) { - dismissKeyPreview(); + dismissAllKeyPreviews(); } // Remove any pending messages, except dismissing preview mHandler.cancelKeyTimers(); - mHandler.cancelPopupPreview(); + mHandler.cancelAllShowKeyPreviews(); mKeyboard = keyboard; LatinImeLogger.onSetKeyboard(keyboard); 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. - * @param previewEnabled whether or not to enable the key feedback popup - * @see #isPreviewEnabled() + * @param previewEnabled whether or not to enable the key feedback preview + * @see #isKeyPreviewEnabled() */ - public void setPreviewEnabled(boolean previewEnabled) { - mShowPreview = previewEnabled; + public void setKeyPreviewEnabled(boolean previewEnabled) { + mShowKeyPreview = previewEnabled; } /** - * Returns the enabled state of the key feedback popup. - * @return whether or not the key feedback popup is enabled - * @see #setPreviewEnabled(boolean) + * Returns the enabled state of the key feedback preview + * @return whether or not the key feedback preview is enabled + * @see #setKeyPreviewEnabled(boolean) */ - public boolean isPreviewEnabled() { - return mShowPreview; + public boolean isKeyPreviewEnabled() { + return mShowKeyPreview; } public int getColorScheme() { return mColorScheme; } - public void setPopupOffset(int x, int y) { - mPopupPreviewOffsetX = x; - mPopupPreviewOffsetY = y; - } - /** * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key * 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. - private void dismissKeyPreview() { - for (PointerTracker tracker : mPointerTrackers) - tracker.releaseKey(); - showPreview(KeyDetector.NOT_A_KEY, null); + private void dismissAllKeyPreviews() { + for (PointerTracker tracker : mPointerTrackers) { + tracker.setReleasedKeyGraphics(); + dismissKeyPreview(tracker); + } } @Override - public void showPreview(int keyIndex, PointerTracker tracker) { - int oldKeyIndex = mOldPreviewKeyIndex; - mOldPreviewKeyIndex = keyIndex; - 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); + public void showKeyPreview(int keyIndex, PointerTracker tracker) { + if (mShowKeyPreview || mKeyboard.needSpacebarPreview(keyIndex)) { + mHandler.showKeyPreview(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: Display up to two key previews when the user presses two keys at the same time 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) { - final FrameLayout screenContent = (FrameLayout) getRootView() - .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)); - } + addKeyPreview(mPreviewText); } final Key key = tracker.getKey(keyIndex); // 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. if (key == null || !mInForeground) return; @@ -942,37 +959,35 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { previewIcon != null ? previewIcon : key.getIcon()); 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 mPreviewText.getBackground().setState( 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. // TODO: Adjust position of key previews which touch screen edges if (lp instanceof ViewGroup.MarginLayoutParams) { 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 - mPopupPreviewDisplayedY = popupPreviewY; + // Record key preview position to display mini-keyboard later at the same position + mKeyPreviewDisplayedY = previewY; mPreviewText.setVisibility(VISIBLE); } @@ -1007,29 +1022,26 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { 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. if (mPopupLayout == 0) { return false; } - Key popupKey = tracker.getKey(keyIndex); - if (popupKey == null) + Key parentKey = tracker.getKey(keyIndex); + if (parentKey == null) return false; - boolean result = onLongPress(popupKey, tracker); + boolean result = onLongPress(parentKey, tracker); if (result) { - dismissKeyPreview(); + dismissAllKeyPreviews(); mMiniKeyboardTrackerId = tracker.mPointerId; - // Mark this tracker "already processed" and remove it from the pointer queue - tracker.setAlreadyProcessed(); - mPointerQueue.remove(tracker); + tracker.onLongPressed(mPointerQueue); } return result; } private void onLongPressShiftKey(PointerTracker tracker) { - tracker.setAlreadyProcessed(); - mPointerQueue.remove(tracker); + tracker.onLongPressed(mPointerQueue); 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); } - private View inflateMiniKeyboardContainer(Key popupKey) { + private View inflateMiniKeyboardContainer(Key parentKey) { final View container = LayoutInflater.from(getContext()).inflate(mPopupLayout, null); if (container == null) throw new NullPointerException(); @@ -1051,19 +1063,19 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { @Override public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) { mKeyboardActionListener.onCodeInput(primaryCode, keyCodes, x, y); - dismissPopupKeyboard(); + dismissMiniKeyboard(); } @Override public void onTextInput(CharSequence text) { mKeyboardActionListener.onTextInput(text); - dismissPopupKeyboard(); + dismissMiniKeyboard(); } @Override public void onCancelInput() { mKeyboardActionListener.onCancelInput(); - dismissPopupKeyboard(); + dismissMiniKeyboard(); } @Override @@ -1085,7 +1097,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { miniKeyboardView.mGestureDetector = null; final Keyboard keyboard = new MiniKeyboardBuilder(this, mKeyboard.getPopupKeyboardResId(), - popupKey).build(); + parentKey).build(); miniKeyboardView.setKeyboard(keyboard); 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. - * @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 * method on the base class if the subclass doesn't wish to handle the call. */ - protected boolean onLongPress(Key popupKey, PointerTracker tracker) { - if (popupKey.mPopupCharacters == null) + protected boolean onLongPress(Key parentKey, PointerTracker tracker) { + if (parentKey.mPopupCharacters == null) return false; - View container = mMiniKeyboardCache.get(popupKey); + View container = mMiniKeyboardCache.get(parentKey); if (container == null) { - container = inflateMiniKeyboardContainer(popupKey); - mMiniKeyboardCache.put(popupKey, container); + container = inflateMiniKeyboardContainer(parentKey); + mMiniKeyboardCache.put(parentKey, container); } mMiniKeyboardView = (KeyboardView)container.findViewById(R.id.KeyboardView); final MiniKeyboard miniKeyboard = (MiniKeyboard)mMiniKeyboardView.getKeyboard(); @@ -1131,36 +1143,35 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { getLocationInWindow(mWindowOffset); } final int pointX = (mConfigShowMiniKeyboardAtTouchedPoint) ? tracker.getLastX() - : popupKey.mX + popupKey.mWidth / 2; - final int popupX = pointX - miniKeyboard.getDefaultCoordX() + : parentKey.mX + parentKey.mWidth / 2; + final int miniKeyboardX = pointX - miniKeyboard.getDefaultCoordX() - container.getPaddingLeft() + getPaddingLeft() + mWindowOffset[0]; - final int popupY = popupKey.mY - mKeyboard.getVerticalGap() + final int miniKeyboardY = parentKey.mY - mKeyboard.getVerticalGap() - (container.getMeasuredHeight() - container.getPaddingBottom()) + getPaddingTop() + mWindowOffset[1]; - final int x = popupX; - final int y = mShowPreview && isOneRowKeys(miniKeyboard.getKeys()) - ? mPopupPreviewDisplayedY : popupY; + final int x = miniKeyboardX; + final int y = mShowKeyPreview && isOneRowKeys(miniKeyboard.getKeys()) + ? mKeyPreviewDisplayedY : miniKeyboardY; mMiniKeyboardOriginX = x + container.getPaddingLeft() - mWindowOffset[0]; mMiniKeyboardOriginY = y + container.getPaddingTop() - mWindowOffset[1]; - mMiniKeyboardView.setPopupOffset(x, y); if (miniKeyboard.setShifted( mKeyboard == null ? false : mKeyboard.isShiftedOrShiftLocked())) { mMiniKeyboardView.invalidateAllKeys(); } // Mini keyboard needs no pop-up key preview displayed. - mMiniKeyboardView.setPreviewEnabled(false); - mMiniKeyboardPopup.setContentView(container); - mMiniKeyboardPopup.setWidth(container.getMeasuredWidth()); - mMiniKeyboardPopup.setHeight(container.getMeasuredHeight()); - mMiniKeyboardPopup.showAtLocation(this, Gravity.NO_GRAVITY, x, y); + mMiniKeyboardView.setKeyPreviewEnabled(false); + mMiniKeyboardWindow.setContentView(container); + mMiniKeyboardWindow.setWidth(container.getMeasuredWidth()); + mMiniKeyboardWindow.setHeight(container.getMeasuredHeight()); + mMiniKeyboardWindow.showAtLocation(this, Gravity.NO_GRAVITY, x, y); // Inject down event on the key to mini keyboard. final long eventTime = SystemClock.uptimeMillis(); - mMiniKeyboardPopupTime = eventTime; + mMiniKeyboardDisplayedTime = eventTime; final MotionEvent downEvent = generateMiniKeyboardMotionEvent(MotionEvent.ACTION_DOWN, - pointX, popupKey.mY + popupKey.mHeight / 2, eventTime); + pointX, parentKey.mY + parentKey.mHeight / 2, eventTime); mMiniKeyboardView.onTouchEvent(downEvent); 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) { - return MotionEvent.obtain(mMiniKeyboardPopupTime, eventTime, action, + return MotionEvent.obtain(mMiniKeyboardDisplayedTime, eventTime, action, 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. for (int i = pointers.size(); i <= id; i++) { final PointerTracker tracker = - new PointerTracker(i, mHandler, mKeyDetector, this, getResources()); + new PointerTracker(i, this, mHandler, mKeyDetector, this); if (mKeyboard != null) tracker.setKeyboard(mKeyboard, mKeyHysteresisDistance); if (listener != null) @@ -1226,7 +1237,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { // TODO: Reconcile gesture detection and accessibility features. if (mMiniKeyboardView == null && !mIsAccessibilityEnabled && mGestureDetector != null && mGestureDetector.onTouchEvent(me)) { - dismissKeyPreview(); + dismissAllKeyPreviews(); mHandler.cancelKeyTimers(); return true; } @@ -1323,7 +1334,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { mPreviewText.setVisibility(View.GONE); mHandler.cancelAllMessages(); - dismissPopupKeyboard(); + dismissMiniKeyboard(); mDirtyRect.union(0, 0, getWidth(), getHeight()); mMiniKeyboardCache.clear(); requestLayout(); @@ -1340,9 +1351,9 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { closing(); } - private void dismissPopupKeyboard() { - if (mMiniKeyboardPopup.isShowing()) { - mMiniKeyboardPopup.dismiss(); + private void dismissMiniKeyboard() { + if (mMiniKeyboardWindow.isShowing()) { + mMiniKeyboardWindow.dismiss(); mMiniKeyboardView = null; mMiniKeyboardOriginX = 0; mMiniKeyboardOriginY = 0; @@ -1351,8 +1362,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { } public boolean handleBack() { - if (mMiniKeyboardPopup.isShowing()) { - dismissPopupKeyboard(); + if (mMiniKeyboardWindow.isShowing()) { + dismissMiniKeyboard(); return true; } return false; diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index d6c3723fd..912074515 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -55,14 +55,14 @@ public class LatinKeyboardView extends KeyboardView { } @Override - public void setPreviewEnabled(boolean previewEnabled) { + public void setKeyPreviewEnabled(boolean previewEnabled) { LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard != null && (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard())) { // Phone and number keyboard never shows popup preview (except language switch). - super.setPreviewEnabled(false); + super.setKeyPreviewEnabled(false); } else { - super.setPreviewEnabled(previewEnabled); + super.setKeyPreviewEnabled(previewEnabled); } } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 5d137b9f6..64f2f9644 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -37,7 +37,8 @@ public class PointerTracker { public interface UIProxy { 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 isAccessibilityEnabled(); } @@ -49,9 +50,7 @@ public class PointerTracker { private final int mLongPressKeyTimeout; private final int mLongPressShiftKeyTimeout; - // Miscellaneous constants - private static final int NOT_A_KEY = KeyDetector.NOT_A_KEY; - + private final KeyboardView mKeyboardView; private final UIProxy mProxy; private final UIHandler mHandler; private final KeyDetector mKeyDetector; @@ -91,9 +90,6 @@ public class PointerTracker { // ignore modifier key if true private boolean mIgnoreModifierKey; - // pressed key - private int mPreviousKey = NOT_A_KEY; - // Empty {@link KeyboardActionListener} private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener() { @Override @@ -110,11 +106,12 @@ public class PointerTracker { public void onSwipeDown() {} }; - public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, - UIProxy proxy, Resources res) { + public PointerTracker(int id, KeyboardView keyboardView, UIHandler handler, + KeyDetector keyDetector, UIProxy proxy) { if (proxy == null || handler == null || keyDetector == null) throw new NullPointerException(); mPointerId = id; + mKeyboardView = keyboardView; mProxy = proxy; mHandler = handler; mKeyDetector = keyDetector; @@ -122,6 +119,7 @@ public class PointerTracker { mKeyState = new PointerTrackerKeyState(keyDetector); mIsAccessibilityEnabled = proxy.isAccessibilityEnabled(); mHasDistinctMultitouch = proxy.hasDistinctMultitouch(); + final Resources res = mKeyboardView.getResources(); mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled); mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start); 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; } - public void releaseKey() { - updateKeyGraphics(NOT_A_KEY); + public void setReleasedKeyGraphics() { + setReleasedKeyGraphics(mKeyState.getKeyIndex()); } - private void updateKeyGraphics(int keyIndex) { - int oldKeyIndex = mPreviousKey; - mPreviousKey = keyIndex; - if (keyIndex != oldKeyIndex) { - if (isValidKeyIndex(oldKeyIndex)) { - 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); - } + private void setReleasedKeyGraphics(int keyIndex) { + final Key key = getKey(keyIndex); + if (key != null) { + key.onReleased(); + mProxy.invalidateKey(key); } } - public void setAlreadyProcessed() { - mKeyAlreadyProcessed = true; + private void setPressedKeyGraphics(int keyIndex) { + final Key key = getKey(keyIndex); + if (key != null && key.mEnabled) { + key.onPressed(); + mProxy.invalidateKey(key); + } } private void checkAssertion(PointerTrackerQueue queue) { @@ -318,7 +311,7 @@ public class PointerTracker { if (DEBUG_MODE) Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT + " distance=" + distanceSquared); - setAlreadyProcessed(); + mKeyAlreadyProcessed = true; return; } } @@ -346,24 +339,25 @@ public class PointerTracker { mIsRepeatableKey = false; mIsInSlidingKeyInput = false; mIgnoreModifierKey = false; - final Key key = getKey(keyIndex); - if (key != null) { + if (isValidKeyIndex(keyIndex)) { // 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 // keyboard layout. - if (callListenerOnPressAndCheckKeyboardLayoutChange(key, false)) + if (callListenerOnPressAndCheckKeyboardLayoutChange(getKey(keyIndex), false)) keyIndex = mKeyState.onDownKey(x, y, eventTime); // Accessibility disables key repeat because users may need to pause on a key to hear // its spoken description. - if (key.mRepeatable && !mIsAccessibilityEnabled) { + final Key key = getKey(keyIndex); + if (key != null && key.mRepeatable && !mIsAccessibilityEnabled) { repeatKey(keyIndex); mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this); mIsRepeatableKey = true; } startLongPressTimer(keyIndex); + showKeyPreview(keyIndex); + setPressedKeyGraphics(keyIndex); } - showKeyPreviewAndUpdateKeyGraphics(keyIndex); } private void startSlidingKeyInput(Key key) { @@ -382,8 +376,9 @@ public class PointerTracker { final int lastX = keyState.getLastX(); final int lastY = keyState.getLastY(); + final int oldKeyIndex = keyState.getKeyIndex(); + final Key oldKey = getKey(oldKeyIndex); int keyIndex = keyState.onMoveKey(x, y); - final Key oldKey = getKey(keyState.getKeyIndex()); if (isValidKeyIndex(keyIndex)) { if (oldKey == null) { // 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); keyState.onMoveToNewKey(keyIndex, x, y); startLongPressTimer(keyIndex); + showKeyPreview(keyIndex); + setPressedKeyGraphics(keyIndex); } else if (!isMinorMoveBounce(x, y, keyIndex)) { // 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 // onPress() to notify that the new key is being pressed. + setReleasedKeyGraphics(oldKeyIndex); callListenerOnRelease(oldKey, oldKey.mCode, true); startSlidingKeyInput(oldKey); mHandler.cancelLongPressTimers(); @@ -410,6 +408,8 @@ public class PointerTracker { keyIndex = keyState.onMoveKey(x, y); keyState.onMoveToNewKey(keyIndex, x, y); startLongPressTimer(keyIndex); + setPressedKeyGraphics(keyIndex); + showKeyPreview(keyIndex); } else { // 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 @@ -421,11 +421,12 @@ public class PointerTracker { if (DEBUG_MODE) Log.w(TAG, String.format("onMoveEvent: sudden move is translated to " + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y)); - onUpEventInternal(lastX, lastY, eventTime); + onUpEventInternal(lastX, lastY, eventTime, true); onDownEventInternal(x, y, eventTime); } else { - setAlreadyProcessed(); - showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); + mKeyAlreadyProcessed = true; + dismissKeyPreview(); + setReleasedKeyGraphics(oldKeyIndex); } return; } @@ -434,19 +435,19 @@ public class PointerTracker { if (oldKey != null && !isMinorMoveBounce(x, y, keyIndex)) { // The pointer has been slid out from the previous key, we must call onRelease() to // notify that the previous key has been released. + setReleasedKeyGraphics(oldKeyIndex); callListenerOnRelease(oldKey, oldKey.mCode, true); startSlidingKeyInput(oldKey); mHandler.cancelLongPressTimers(); if (mIsAllowedSlidingKeyInput) { - keyState.onMoveToNewKey(keyIndex, x ,y); + keyState.onMoveToNewKey(keyIndex, x, y); } else { - setAlreadyProcessed(); - showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); + mKeyAlreadyProcessed = true; + dismissKeyPreview(); return; } } } - showKeyPreviewAndUpdateKeyGraphics(keyState.getKeyIndex()); } public void onUpEvent(int x, int y, long eventTime, PointerTrackerQueue queue) { @@ -464,35 +465,48 @@ public class PointerTracker { } queue.remove(this); } - onUpEventInternal(x, y, eventTime); + onUpEventInternal(x, y, eventTime, true); } - public void onUpEventForRelease(int x, int y, long eventTime) { - onUpEventInternal(x, y, eventTime); + // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event. + // 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) { - int x = pointX; - int y = pointY; + private void onUpEventInternal(int x, int y, long eventTime, + boolean updateReleasedKeyGraphics) { mHandler.cancelKeyTimers(); - mHandler.cancelPopupPreview(); - showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); + mHandler.cancelShowKeyPreview(this); 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) 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) { - 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) { if (ENABLE_ASSERTION) checkAssertion(queue); if (DEBUG_EVENT) @@ -505,8 +519,9 @@ public class PointerTracker { private void onCancelEventInternal() { mHandler.cancelKeyTimers(); - mHandler.cancelPopupPreview(); - showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); + mHandler.cancelShowKeyPreview(this); + dismissKeyPreview(); + setReleasedKeyGraphics(mKeyState.getKeyIndex()); mIsInSlidingKeyInput = false; } @@ -542,7 +557,7 @@ public class PointerTracker { } } - private void showKeyPreviewAndUpdateKeyGraphics(int keyIndex) { + private void showKeyPreview(int keyIndex) { final Key key = getKey(keyIndex); if (key != null && !key.mEnabled) return; @@ -550,12 +565,13 @@ public class PointerTracker { // 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 // preview. - if (mHasDistinctMultitouch && isModifier() && !mIsAccessibilityEnabled) { - mProxy.showPreview(NOT_A_KEY, this); - } else { - mProxy.showPreview(keyIndex, this); - } - updateKeyGraphics(keyIndex); + if (mHasDistinctMultitouch && isModifier() && !mIsAccessibilityEnabled) + return; + mProxy.showKeyPreview(keyIndex, this); + } + + private void dismissKeyPreview() { + mProxy.dismissKeyPreview(this); } private void startLongPressTimer(int keyIndex) { diff --git a/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java b/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java index a62ed96a3..b3ed1e26f 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java @@ -92,6 +92,7 @@ package com.android.inputmethod.keyboard; public int onUpKey(int x, int y, long eventTime) { mUpTime = eventTime; + mKeyIndex = KeyDetector.NOT_A_KEY; return onMoveKeyInternal(x, y); } } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java index 928f3cdc1..0a9410042 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java @@ -29,14 +29,13 @@ public class PointerTrackerQueue { if (mQueue.lastIndexOf(tracker) < 0) { return; } - LinkedList queue = mQueue; + final LinkedList queue = mQueue; int oldestPos = 0; for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) { if (t.isModifier()) { oldestPos++; } else { - t.onUpEventForRelease(t.getLastX(), t.getLastY(), eventTime); - t.setAlreadyProcessed(); + t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime); queue.remove(oldestPos); } } @@ -50,8 +49,7 @@ public class PointerTrackerQueue { for (PointerTracker t : mQueue) { if (t == tracker) continue; - t.onUpEventForRelease(t.getLastX(), t.getLastY(), eventTime); - t.setAlreadyProcessed(); + t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime); } mQueue.clear(); if (tracker != null) diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java index be5e015aa..40ab28c98 100644 --- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java +++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java @@ -162,36 +162,42 @@ public class InputLanguageSelection extends PreferenceActivity { for (int i = 0 ; i < origSize; i++ ) { String s = locales[i]; int len = s.length(); + String language = ""; + String country = ""; if (len == 5) { - String language = s.substring(0, 2); - String country = s.substring(3, 5); - Locale l = new Locale(language, country); + language = s.substring(0, 2); + country = s.substring(3, 5); + } else if (len < 5) { + language = s; + } + Locale l = new Locale(language, country); - // Exclude languages that are not relevant to LatinIME - if (arrayContains(BLACKLIST_LANGUAGES, language)) continue; + // Exclude languages that are not relevant to LatinIME + if (arrayContains(BLACKLIST_LANGUAGES, language) || TextUtils.isEmpty(language)) { + continue; + } - if (finalSize == 0) { + if (finalSize == 0) { + preprocess[finalSize++] = + new Loc(SubtypeSwitcher.getFullDisplayName(l, true), l); + } else { + // check previous entry: + // same lang and a country -> upgrade to full name and + // insert ours with full name + // diff lang -> insert ours with lang-only name + if (preprocess[finalSize-1].mLocale.getLanguage().equals( + language)) { + preprocess[finalSize-1].setLabel(SubtypeSwitcher.getFullDisplayName( + preprocess[finalSize-1].mLocale, false)); preprocess[finalSize++] = - new Loc(SubtypeSwitcher.getFullDisplayName(l, true), l); + new Loc(SubtypeSwitcher.getFullDisplayName(l, false), l); } else { - // check previous entry: - // same lang and a country -> upgrade to full name and - // insert ours with full name - // diff lang -> insert ours with lang-only name - if (preprocess[finalSize-1].mLocale.getLanguage().equals( - language)) { - preprocess[finalSize-1].setLabel(SubtypeSwitcher.getFullDisplayName( - preprocess[finalSize-1].mLocale, false)); - preprocess[finalSize++] = - new Loc(SubtypeSwitcher.getFullDisplayName(l, false), l); + String displayName; + if (s.equals("zz_ZZ")) { + // ignore this locale } else { - String displayName; - if (s.equals("zz_ZZ")) { - // ignore this locale - } else { - displayName = SubtypeSwitcher.getFullDisplayName(l, true); - preprocess[finalSize++] = new Loc(displayName, l); - } + displayName = SubtypeSwitcher.getFullDisplayName(l, true); + preprocess[finalSize++] = new Loc(displayName, l); } } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 88b3ded8e..13ef4ffe8 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -592,7 +592,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final boolean accessibilityEnabled = mAccessibilityUtils.isAccessibilityEnabled(); - inputView.setPreviewEnabled(mPopupOn); + inputView.setKeyPreviewEnabled(mPopupOn); inputView.setProximityCorrectionEnabled(true); inputView.setAccessibilityEnabled(accessibilityEnabled); // If we just entered a text field, maybe it has some old text that requires correction diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp index 8e82f1474..3832d473a 100644 --- a/native/src/unigram_dictionary.cpp +++ b/native/src/unigram_dictionary.cpp @@ -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, const int firstWordStartPos, const int firstWordLength, const int secondWordStartPos, const int secondWordLength) { @@ -448,15 +496,12 @@ bool UnigramDictionary::getSplitTwoWordsSuggestion(const int inputLength, word[i] = mWord[i - firstWordLength - 1]; } - // Promote pairFreq with multiplying by 2, because the word length is the same as the typed - // length. - int pairFreq = firstFreq + secondFreq; - for (int i = 0; i < inputLength; ++i) pairFreq *= TYPED_LETTER_MULTIPLIER; + int pairFreq = calcFreqForSplitTwoWords( + TYPED_LETTER_MULTIPLIER, firstWordLength, secondWordLength, firstFreq, secondFreq); if (DEBUG_DICT) { LOGI("Missing space: %d, %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength, TYPED_LETTER_MULTIPLIER); } - multiplyRate(WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE, &pairFreq); addWord(word, newWordLength, pairFreq); return true; }