aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyDetector.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboard.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java681
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java739
-rw-r--r--java/src/com/android/inputmethod/keyboard/MiniKeyboard.java250
-rw-r--r--java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java59
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java56
-rw-r--r--java/src/com/android/inputmethod/keyboard/PopupPanel.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java158
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java259
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java7
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFactory.java22
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java66
16 files changed, 1120 insertions, 1201 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index 3dca9aae6..8185619f9 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -35,7 +35,7 @@ import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.compat.MotionEventCompatUtils;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector;
-import com.android.inputmethod.keyboard.LatinKeyboardBaseView;
+import com.android.inputmethod.keyboard.LatinKeyboardView;
import com.android.inputmethod.keyboard.PointerTracker;
public class AccessibleKeyboardViewProxy {
@@ -47,7 +47,7 @@ public class AccessibleKeyboardViewProxy {
private InputMethodService mInputMethod;
private FlickGestureDetector mGestureDetector;
- private LatinKeyboardBaseView mView;
+ private LatinKeyboardView mView;
private AccessibleKeyboardActionListener mListener;
private AudioManagerCompatWrapper mAudioManager;
@@ -65,7 +65,7 @@ public class AccessibleKeyboardViewProxy {
return sInstance;
}
- public static void setView(LatinKeyboardBaseView view) {
+ public static void setView(LatinKeyboardView view) {
sInstance.mView = view;
}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 397b7b16b..f56b52388 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -295,12 +295,8 @@ public class Key {
mY = y;
mWidth = keyWidth - mHorizontalGap;
- CharSequence[] popupCharacters = style.getTextArray(
+ final CharSequence[] popupCharacters = style.getTextArray(
keyAttr, R.styleable.Keyboard_Key_popupCharacters);
- if (params.mId.mPasswordInput) {
- popupCharacters = PopupCharactersParser.filterOut(
- res, popupCharacters, PopupCharactersParser.NON_ASCII_FILTER);
- }
// In Arabic symbol layouts, we'd like to keep digits in popup characters regardless of
// config_digit_popup_characters_enabled.
if (params.mId.isAlphabetKeyboard() && !res.getBoolean(
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 0a3acb48b..3298c41cf 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -91,6 +91,10 @@ public class KeyDetector {
mProximityThresholdSquare = threshold * threshold;
}
+ public boolean alwaysAllowsSlidingInput() {
+ return false;
+ }
+
/**
* Computes maximum size of the array that can contain all nearby key indices returned by
* {@link #getKeyIndexAndNearbyCodes}.
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index 3b3e1f87e..1b6f57b92 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -141,7 +141,7 @@ public class LatinKeyboard extends Keyboard {
}
}
- public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboardBaseView view) {
+ public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboardView view) {
mSpacebarTextFadeFactor = fadeFactor;
updateSpacebarForLocale(false);
if (view != null)
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
deleted file mode 100644
index 4a7b2bd60..000000000
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
+++ /dev/null
@@ -1,681 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.PopupWindow;
-
-import com.android.inputmethod.accessibility.AccessibilityUtils;
-import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
-import com.android.inputmethod.deprecated.VoiceProxy;
-import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
-import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
-import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder;
-import com.android.inputmethod.latin.LatinIME;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
-import com.android.inputmethod.latin.Utils;
-
-import java.util.WeakHashMap;
-
-/**
- * A view that is responsible for detecting key presses and touch movements.
- *
- * @attr ref R.styleable#KeyboardView_keyHysteresisDistance
- * @attr ref R.styleable#KeyboardView_verticalCorrection
- * @attr ref R.styleable#KeyboardView_popupLayout
- */
-public class LatinKeyboardBaseView extends KeyboardView implements PointerTracker.KeyEventHandler {
- private static final String TAG = LatinKeyboardBaseView.class.getSimpleName();
-
- private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true;
-
- // Timing constants
- private final int mKeyRepeatInterval;
-
- // XML attribute
- private final float mVerticalCorrection;
- private final int mPopupLayout;
-
- // Mini keyboard
- private PopupWindow mPopupWindow;
- private PopupPanel mPopupPanel;
- private int mPopupPanelPointerTrackerId;
- private final WeakHashMap<Key, PopupPanel> mPopupPanelCache =
- new WeakHashMap<Key, PopupPanel>();
-
- /** Listener for {@link KeyboardActionListener}. */
- private KeyboardActionListener mKeyboardActionListener;
-
- private final boolean mHasDistinctMultitouch;
- private int mOldPointerCount = 1;
- private int mOldKeyIndex;
-
- protected KeyDetector mKeyDetector;
-
- // To detect double tap.
- protected GestureDetector mGestureDetector;
-
- private final KeyTimerHandler mKeyTimerHandler = new KeyTimerHandler(this);
-
- private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardBaseView>
- implements TimerProxy {
- private static final int MSG_REPEAT_KEY = 1;
- private static final int MSG_LONGPRESS_KEY = 2;
- private static final int MSG_IGNORE_DOUBLE_TAP = 3;
-
- private boolean mInKeyRepeat;
-
- public KeyTimerHandler(LatinKeyboardBaseView outerInstance) {
- super(outerInstance);
- }
-
- @Override
- public void handleMessage(Message msg) {
- final LatinKeyboardBaseView keyboardView = getOuterInstance();
- final PointerTracker tracker = (PointerTracker) msg.obj;
- switch (msg.what) {
- case MSG_REPEAT_KEY:
- tracker.onRepeatKey(msg.arg1);
- startKeyRepeatTimer(keyboardView.mKeyRepeatInterval, msg.arg1, tracker);
- break;
- case MSG_LONGPRESS_KEY:
- keyboardView.openMiniKeyboardIfRequired(msg.arg1, tracker);
- break;
- }
- }
-
- @Override
- public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {
- mInKeyRepeat = true;
- sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0, tracker), delay);
- }
-
- public void cancelKeyRepeatTimer() {
- mInKeyRepeat = false;
- removeMessages(MSG_REPEAT_KEY);
- }
-
- public boolean isInKeyRepeat() {
- return mInKeyRepeat;
- }
-
- @Override
- public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {
- cancelLongPressTimer();
- sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0, tracker), delay);
- }
-
- @Override
- public void cancelLongPressTimer() {
- removeMessages(MSG_LONGPRESS_KEY);
- }
-
- @Override
- public void cancelKeyTimers() {
- cancelKeyRepeatTimer();
- cancelLongPressTimer();
- removeMessages(MSG_IGNORE_DOUBLE_TAP);
- }
-
- public void startIgnoringDoubleTap() {
- sendMessageDelayed(obtainMessage(MSG_IGNORE_DOUBLE_TAP),
- ViewConfiguration.getDoubleTapTimeout());
- }
-
- public boolean isIgnoringDoubleTap() {
- return hasMessages(MSG_IGNORE_DOUBLE_TAP);
- }
-
- public void cancelAllMessages() {
- cancelKeyTimers();
- }
- }
-
- private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
- private boolean mProcessingShiftDoubleTapEvent = false;
-
- @Override
- public boolean onDoubleTap(MotionEvent firstDown) {
- final Keyboard keyboard = getKeyboard();
- if (ENABLE_CAPSLOCK_BY_DOUBLETAP && keyboard instanceof LatinKeyboard
- && ((LatinKeyboard) keyboard).isAlphaKeyboard()) {
- final int pointerIndex = firstDown.getActionIndex();
- final int id = firstDown.getPointerId(pointerIndex);
- final PointerTracker tracker = getPointerTracker(id);
- // If the first down event is on shift key.
- if (tracker.isOnShiftKey((int) firstDown.getX(), (int) firstDown.getY())) {
- mProcessingShiftDoubleTapEvent = true;
- return true;
- }
- }
- mProcessingShiftDoubleTapEvent = false;
- return false;
- }
-
- @Override
- public boolean onDoubleTapEvent(MotionEvent secondTap) {
- if (mProcessingShiftDoubleTapEvent
- && secondTap.getAction() == MotionEvent.ACTION_DOWN) {
- final MotionEvent secondDown = secondTap;
- final int pointerIndex = secondDown.getActionIndex();
- final int id = secondDown.getPointerId(pointerIndex);
- final PointerTracker tracker = getPointerTracker(id);
- // If the second down event is also on shift key.
- if (tracker.isOnShiftKey((int) secondDown.getX(), (int) secondDown.getY())) {
- // Detected a double tap on shift key. If we are in the ignoring double tap
- // mode, it means we have already turned off caps lock in
- // {@link KeyboardSwitcher#onReleaseShift} .
- final boolean ignoringDoubleTap = mKeyTimerHandler.isIgnoringDoubleTap();
- if (!ignoringDoubleTap)
- onDoubleTapShiftKey(tracker);
- return true;
- }
- // Otherwise these events should not be handled as double tap.
- mProcessingShiftDoubleTapEvent = false;
- }
- return mProcessingShiftDoubleTapEvent;
- }
- }
-
- public LatinKeyboardBaseView(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.keyboardViewStyle);
- }
-
- public LatinKeyboardBaseView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
- mVerticalCorrection = a.getDimensionPixelOffset(
- R.styleable.KeyboardView_verticalCorrection, 0);
- mPopupLayout = a.getResourceId(R.styleable.KeyboardView_popupLayout, 0);
- a.recycle();
-
- final Resources res = getResources();
- final float keyHysteresisDistance = res.getDimension(R.dimen.key_hysteresis_distance);
- mKeyDetector = new KeyDetector(keyHysteresisDistance);
-
- final boolean ignoreMultitouch = true;
- mGestureDetector = new GestureDetector(
- getContext(), new DoubleTapListener(), null, ignoreMultitouch);
- mGestureDetector.setIsLongpressEnabled(false);
-
- mHasDistinctMultitouch = context.getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
- mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
-
- PointerTracker.init(mHasDistinctMultitouch, getContext());
- }
-
- public void startIgnoringDoubleTap() {
- if (ENABLE_CAPSLOCK_BY_DOUBLETAP)
- mKeyTimerHandler.startIgnoringDoubleTap();
- }
-
- public void setKeyboardActionListener(KeyboardActionListener listener) {
- mKeyboardActionListener = listener;
- PointerTracker.setKeyboardActionListener(listener);
- }
-
- /**
- * Returns the {@link KeyboardActionListener} object.
- * @return the listener attached to this keyboard
- */
- @Override
- public KeyboardActionListener getKeyboardActionListener() {
- return mKeyboardActionListener;
- }
-
- @Override
- public KeyDetector getKeyDetector() {
- return mKeyDetector;
- }
-
- @Override
- public DrawingProxy getDrawingProxy() {
- return this;
- }
-
- @Override
- public TimerProxy getTimerProxy() {
- return mKeyTimerHandler;
- }
-
- @Override
- public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
- final Keyboard keyboard = getKeyboard();
- if (keyboard instanceof LatinKeyboard) {
- final LatinKeyboard latinKeyboard = (LatinKeyboard)keyboard;
- if (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard()) {
- // Phone and number keyboard never shows popup preview.
- super.setKeyPreviewPopupEnabled(false, delay);
- return;
- }
- }
- super.setKeyPreviewPopupEnabled(previewEnabled, delay);
- }
-
- /**
- * Attaches a keyboard to this view. The keyboard can be switched at any time and the
- * view will re-layout itself to accommodate the keyboard.
- * @see Keyboard
- * @see #getKeyboard()
- * @param keyboard the keyboard to display in this view
- */
- @Override
- public void setKeyboard(Keyboard keyboard) {
- // Remove any pending messages, except dismissing preview
- mKeyTimerHandler.cancelKeyTimers();
- super.setKeyboard(keyboard);
- mKeyDetector.setKeyboard(
- keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
- mKeyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
- PointerTracker.setKeyDetector(mKeyDetector);
- mPopupPanelCache.clear();
- }
-
- /**
- * Returns whether the device has distinct multi-touch panel.
- * @return true if the device has distinct multi-touch panel.
- */
- public boolean hasDistinctMultitouch() {
- return mHasDistinctMultitouch;
- }
-
- /**
- * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key
- * codes for adjacent keys. When disabled, only the primary key code will be
- * reported.
- * @param enabled whether or not the proximity correction is enabled
- */
- public void setProximityCorrectionEnabled(boolean enabled) {
- mKeyDetector.setProximityCorrectionEnabled(enabled);
- }
-
- /**
- * Returns true if proximity correction is enabled.
- */
- public boolean isProximityCorrectionEnabled() {
- return mKeyDetector.isProximityCorrectionEnabled();
- }
-
- @Override
- public void cancelAllMessages() {
- mKeyTimerHandler.cancelAllMessages();
- super.cancelAllMessages();
- }
-
- private boolean openMiniKeyboardIfRequired(int keyIndex, PointerTracker tracker) {
- // Check if we have a popup layout specified first.
- if (mPopupLayout == 0) {
- return false;
- }
-
- // Check if we are already displaying popup panel.
- if (mPopupPanel != null)
- return false;
- final Key parentKey = tracker.getKey(keyIndex);
- if (parentKey == null)
- return false;
- return onLongPress(parentKey, tracker);
- }
-
- private void onDoubleTapShiftKey(@SuppressWarnings("unused") PointerTracker tracker) {
- // When shift key is double tapped, the first tap is correctly processed as usual tap. And
- // the second tap is treated as this double tap event, so that we need not mark tracker
- // calling setAlreadyProcessed() nor remove the tracker from mPointerQueue.
- mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
- }
-
- // This default implementation returns a popup mini keyboard panel.
- protected PopupPanel onCreatePopupPanel(Key parentKey) {
- if (parentKey.mPopupCharacters == null)
- return null;
-
- final View container = LayoutInflater.from(getContext()).inflate(mPopupLayout, null);
- if (container == null)
- throw new NullPointerException();
-
- final PopupMiniKeyboardView miniKeyboardView =
- (PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view);
- final Keyboard parentKeyboard = getKeyboard();
- final Keyboard miniKeyboard = new MiniKeyboardBuilder(
- this, parentKeyboard.mPopupKeyboardResId, parentKey, parentKeyboard).build();
- miniKeyboardView.setKeyboard(miniKeyboard);
-
- container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-
- return miniKeyboardView;
- }
-
- @Override
- protected boolean needsToDimKeyboard() {
- return mPopupPanel != null;
- }
-
- public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboard oldKeyboard) {
- final Keyboard keyboard = getKeyboard();
- // We should not set text fade factor to the keyboard which does not display the language on
- // its spacebar.
- if (keyboard instanceof LatinKeyboard && keyboard == oldKeyboard) {
- ((LatinKeyboard)keyboard).setSpacebarTextFadeFactor(fadeFactor, this);
- }
- }
-
- /**
- * Called when a key is long pressed. By default this will open mini keyboard associated
- * with this key.
- * @param parentKey the key that was long pressed
- * @param tracker the pointer tracker which pressed the parent key
- * @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 parentKey, PointerTracker tracker) {
- final int primaryCode = parentKey.mCode;
- final Keyboard keyboard = getKeyboard();
- if (keyboard instanceof LatinKeyboard) {
- final LatinKeyboard latinKeyboard = (LatinKeyboard) keyboard;
- if (primaryCode == Keyboard.CODE_DIGIT0 && latinKeyboard.isPhoneKeyboard()) {
- tracker.onLongPressed();
- // Long pressing on 0 in phone number keypad gives you a '+'.
- return invokeOnKey(Keyboard.CODE_PLUS);
- }
- if (primaryCode == Keyboard.CODE_SHIFT && latinKeyboard.isAlphaKeyboard()) {
- tracker.onLongPressed();
- return invokeOnKey(Keyboard.CODE_CAPSLOCK);
- }
- }
- if (primaryCode == Keyboard.CODE_SETTINGS || primaryCode == Keyboard.CODE_SPACE) {
- // Both long pressing settings key and space key invoke IME switcher dialog.
- if (getKeyboardActionListener().onCustomRequest(
- LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
- tracker.onLongPressed();
- return true;
- } else {
- return openPopupPanel(parentKey, tracker);
- }
- } else {
- return openPopupPanel(parentKey, tracker);
- }
- }
-
- private boolean invokeOnKey(int primaryCode) {
- getKeyboardActionListener().onCodeInput(primaryCode, null,
- KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
- KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
- return true;
- }
-
- private boolean openPopupPanel(Key parentKey, PointerTracker tracker) {
- PopupPanel popupPanel = mPopupPanelCache.get(parentKey);
- if (popupPanel == null) {
- popupPanel = onCreatePopupPanel(parentKey);
- if (popupPanel == null)
- return false;
- mPopupPanelCache.put(parentKey, popupPanel);
- }
- if (mPopupWindow == null) {
- mPopupWindow = new PopupWindow(getContext());
- mPopupWindow.setBackgroundDrawable(null);
- mPopupWindow.setAnimationStyle(R.style.PopupMiniKeyboardAnimation);
- // Allow popup window to be drawn off the screen.
- mPopupWindow.setClippingEnabled(false);
- }
- mPopupPanel = popupPanel;
- mPopupPanelPointerTrackerId = tracker.mPointerId;
-
- popupPanel.showPopupPanel(this, parentKey, tracker, mPopupWindow);
- final int translatedX = popupPanel.translateX(tracker.getLastX());
- final int translatedY = popupPanel.translateY(tracker.getLastY());
- tracker.onShowPopupPanel(translatedX, translatedY, SystemClock.uptimeMillis(), popupPanel);
-
- invalidateAllKeys();
- return true;
- }
-
- private PointerTracker getPointerTracker(final int id) {
- return PointerTracker.getPointerTracker(id, this);
- }
-
- public boolean isInSlidingKeyInput() {
- if (mPopupPanel != null) {
- return true;
- } else {
- return PointerTracker.isAnyInSlidingKeyInput();
- }
- }
-
- public int getPointerCount() {
- return mOldPointerCount;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent me) {
- final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
- final int action = me.getActionMasked();
- final int pointerCount = me.getPointerCount();
- final int oldPointerCount = mOldPointerCount;
- mOldPointerCount = pointerCount;
-
- // TODO: cleanup this code into a multi-touch to single-touch event converter class?
- // If the device does not have distinct multi-touch support panel, ignore all multi-touch
- // events except a transition from/to single-touch.
- if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
- return true;
- }
-
- // Gesture detector must be enabled only when mini-keyboard is not on the screen.
- if (mPopupPanel == null && mGestureDetector != null
- && mGestureDetector.onTouchEvent(me)) {
- PointerTracker.dismissAllKeyPreviews();
- mKeyTimerHandler.cancelKeyTimers();
- return true;
- }
-
- final long eventTime = me.getEventTime();
- final int index = me.getActionIndex();
- final int id = me.getPointerId(index);
- final int x, y;
- if (mPopupPanel != null && id == mPopupPanelPointerTrackerId) {
- x = mPopupPanel.translateX((int)me.getX(index));
- y = mPopupPanel.translateY((int)me.getY(index));
- } else {
- x = (int)me.getX(index);
- y = (int)me.getY(index);
- }
-
- if (mKeyTimerHandler.isInKeyRepeat()) {
- final PointerTracker tracker = getPointerTracker(id);
- // Key repeating timer will be canceled if 2 or more keys are in action, and current
- // event (UP or DOWN) is non-modifier key.
- if (pointerCount > 1 && !tracker.isModifier()) {
- mKeyTimerHandler.cancelKeyRepeatTimer();
- }
- // Up event will pass through.
- }
-
- // TODO: cleanup this code into a multi-touch to single-touch event converter class?
- // Translate mutli-touch event to single-touch events on the device that has no distinct
- // multi-touch panel.
- if (nonDistinctMultitouch) {
- // Use only main (id=0) pointer tracker.
- PointerTracker tracker = getPointerTracker(0);
- if (pointerCount == 1 && oldPointerCount == 2) {
- // Multi-touch to single touch transition.
- // Send a down event for the latest pointer if the key is different from the
- // previous key.
- final int newKeyIndex = tracker.getKeyIndexOn(x, y);
- if (mOldKeyIndex != newKeyIndex) {
- tracker.onDownEvent(x, y, eventTime, this);
- if (action == MotionEvent.ACTION_UP)
- tracker.onUpEvent(x, y, eventTime);
- }
- } else if (pointerCount == 2 && oldPointerCount == 1) {
- // Single-touch to multi-touch transition.
- // Send an up event for the last pointer.
- final int lastX = tracker.getLastX();
- final int lastY = tracker.getLastY();
- mOldKeyIndex = tracker.getKeyIndexOn(lastX, lastY);
- tracker.onUpEvent(lastX, lastY, eventTime);
- } else if (pointerCount == 1 && oldPointerCount == 1) {
- processMotionEvent(tracker, action, x, y, eventTime, this);
- } else {
- Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
- + " (old " + oldPointerCount + ")");
- }
- return true;
- }
-
- if (action == MotionEvent.ACTION_MOVE) {
- for (int i = 0; i < pointerCount; i++) {
- final PointerTracker tracker = getPointerTracker(me.getPointerId(i));
- final int px, py;
- if (mPopupPanel != null && tracker.mPointerId == mPopupPanelPointerTrackerId) {
- px = mPopupPanel.translateX((int)me.getX(i));
- py = mPopupPanel.translateY((int)me.getY(i));
- } else {
- px = (int)me.getX(i);
- py = (int)me.getY(i);
- }
- tracker.onMoveEvent(px, py, eventTime);
- }
- } else {
- processMotionEvent(getPointerTracker(id), action, x, y, eventTime, this);
- }
-
- return true;
- }
-
- private static void processMotionEvent(PointerTracker tracker, int action, int x, int y,
- long eventTime, PointerTracker.KeyEventHandler handler) {
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN:
- tracker.onDownEvent(x, y, eventTime, handler);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP:
- tracker.onUpEvent(x, y, eventTime);
- break;
- case MotionEvent.ACTION_MOVE:
- tracker.onMoveEvent(x, y, eventTime);
- break;
- case MotionEvent.ACTION_CANCEL:
- tracker.onCancelEvent(x, y, eventTime);
- break;
- }
- }
-
- @Override
- public void closing() {
- super.closing();
- dismissPopupPanel();
- mPopupPanelCache.clear();
- }
-
- @Override
- public boolean dismissPopupPanel() {
- if (mPopupWindow != null && mPopupWindow.isShowing()) {
- mPopupWindow.dismiss();
- mPopupPanel = null;
- mPopupPanelPointerTrackerId = -1;
- invalidateAllKeys();
- return true;
- }
- return false;
- }
-
- public boolean handleBack() {
- return dismissPopupPanel();
- }
-
- @Override
- public void draw(Canvas c) {
- Utils.GCUtils.getInstance().reset();
- boolean tryGC = true;
- for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
- try {
- super.draw(c);
- tryGC = false;
- } catch (OutOfMemoryError e) {
- tryGC = Utils.GCUtils.getInstance().tryGCOrWait("LatinKeyboardView", e);
- }
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- // Token is available from here.
- VoiceProxy.getInstance().onAttachedToWindow();
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- // Drop non-hover touch events when touch exploration is enabled.
- if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- return false;
- }
-
- return super.dispatchTouchEvent(event);
- }
-
- @Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- final PointerTracker tracker = getPointerTracker(0);
- return AccessibleKeyboardViewProxy.getInstance().dispatchPopulateAccessibilityEvent(
- event, tracker) || super.dispatchPopulateAccessibilityEvent(event);
- }
-
- return super.dispatchPopulateAccessibilityEvent(event);
- }
-
- /**
- * Receives hover events from the input framework. This method overrides
- * View.dispatchHoverEvent(MotionEvent) on SDK version ICS or higher. On
- * lower SDK versions, this method is never called.
- *
- * @param event The motion event to be dispatched.
- * @return {@code true} if the event was handled by the view, {@code false}
- * otherwise
- */
- public boolean dispatchHoverEvent(MotionEvent event) {
- if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- final PointerTracker tracker = getPointerTracker(0);
- return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
- }
-
- // Reflection doesn't support calling superclass methods.
- return false;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 42ce7c440..be04b5a52 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -1,152 +1,691 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.android.inputmethod.keyboard;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.os.Message;
+import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.PopupWindow;
-import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.accessibility.AccessibilityUtils;
+import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
+import com.android.inputmethod.deprecated.VoiceProxy;
+import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
+import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.Utils;
-// TODO: We should remove this class
-public class LatinKeyboardView extends LatinKeyboardBaseView {
+import java.util.WeakHashMap;
+
+/**
+ * A view that is responsible for detecting key presses and touch movements.
+ *
+ * @attr ref R.styleable#KeyboardView_keyHysteresisDistance
+ * @attr ref R.styleable#KeyboardView_verticalCorrection
+ * @attr ref R.styleable#KeyboardView_popupLayout
+ */
+public class LatinKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
+ SuddenJumpingTouchEventHandler.ProcessMotionEvent {
private static final String TAG = LatinKeyboardView.class.getSimpleName();
- private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
- /** Whether we've started dropping move events because we found a big jump */
- private boolean mDroppingEvents;
- /**
- * Whether multi-touch disambiguation needs to be disabled if a real multi-touch event has
- * occured
- */
- private boolean mDisableDisambiguation;
- /** The distance threshold at which we start treating the touch session as a multi-touch */
- private int mJumpThresholdSquare = Integer.MAX_VALUE;
- private int mLastX;
- private int mLastY;
+ private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true;
+
+ private final SuddenJumpingTouchEventHandler mTouchScreenRegulator;
+
+ // Timing constants
+ private final int mKeyRepeatInterval;
+
+ // XML attribute
+ private final float mVerticalCorrection;
+ private final int mPopupLayout;
+
+ // Mini keyboard
+ private PopupWindow mPopupWindow;
+ private PopupPanel mPopupPanel;
+ private int mPopupPanelPointerTrackerId;
+ private final WeakHashMap<Key, PopupPanel> mPopupPanelCache =
+ new WeakHashMap<Key, PopupPanel>();
+
+ /** Listener for {@link KeyboardActionListener}. */
+ private KeyboardActionListener mKeyboardActionListener;
+
+ private final boolean mHasDistinctMultitouch;
+ private int mOldPointerCount = 1;
+ private int mOldKeyIndex;
+
+ protected KeyDetector mKeyDetector;
+
+ // To detect double tap.
+ protected GestureDetector mGestureDetector;
+
+ private final KeyTimerHandler mKeyTimerHandler = new KeyTimerHandler(this);
+
+ private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView>
+ implements TimerProxy {
+ private static final int MSG_REPEAT_KEY = 1;
+ private static final int MSG_LONGPRESS_KEY = 2;
+ private static final int MSG_IGNORE_DOUBLE_TAP = 3;
+
+ private boolean mInKeyRepeat;
+
+ public KeyTimerHandler(LatinKeyboardView outerInstance) {
+ super(outerInstance);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final LatinKeyboardView keyboardView = getOuterInstance();
+ final PointerTracker tracker = (PointerTracker) msg.obj;
+ switch (msg.what) {
+ case MSG_REPEAT_KEY:
+ tracker.onRepeatKey(msg.arg1);
+ startKeyRepeatTimer(keyboardView.mKeyRepeatInterval, msg.arg1, tracker);
+ break;
+ case MSG_LONGPRESS_KEY:
+ keyboardView.openMiniKeyboardIfRequired(msg.arg1, tracker);
+ break;
+ }
+ }
+
+ @Override
+ public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {
+ mInKeyRepeat = true;
+ sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0, tracker), delay);
+ }
+
+ public void cancelKeyRepeatTimer() {
+ mInKeyRepeat = false;
+ removeMessages(MSG_REPEAT_KEY);
+ }
+
+ public boolean isInKeyRepeat() {
+ return mInKeyRepeat;
+ }
+
+ @Override
+ public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {
+ cancelLongPressTimer();
+ sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0, tracker), delay);
+ }
+
+ @Override
+ public void cancelLongPressTimer() {
+ removeMessages(MSG_LONGPRESS_KEY);
+ }
+
+ @Override
+ public void cancelKeyTimers() {
+ cancelKeyRepeatTimer();
+ cancelLongPressTimer();
+ removeMessages(MSG_IGNORE_DOUBLE_TAP);
+ }
+
+ public void startIgnoringDoubleTap() {
+ sendMessageDelayed(obtainMessage(MSG_IGNORE_DOUBLE_TAP),
+ ViewConfiguration.getDoubleTapTimeout());
+ }
+
+ public boolean isIgnoringDoubleTap() {
+ return hasMessages(MSG_IGNORE_DOUBLE_TAP);
+ }
+
+ public void cancelAllMessages() {
+ cancelKeyTimers();
+ }
+ }
+
+ private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
+ private boolean mProcessingShiftDoubleTapEvent = false;
+
+ @Override
+ public boolean onDoubleTap(MotionEvent firstDown) {
+ final Keyboard keyboard = getKeyboard();
+ if (ENABLE_CAPSLOCK_BY_DOUBLETAP && keyboard instanceof LatinKeyboard
+ && ((LatinKeyboard) keyboard).isAlphaKeyboard()) {
+ final int pointerIndex = firstDown.getActionIndex();
+ final int id = firstDown.getPointerId(pointerIndex);
+ final PointerTracker tracker = getPointerTracker(id);
+ // If the first down event is on shift key.
+ if (tracker.isOnShiftKey((int) firstDown.getX(), (int) firstDown.getY())) {
+ mProcessingShiftDoubleTapEvent = true;
+ return true;
+ }
+ }
+ mProcessingShiftDoubleTapEvent = false;
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTapEvent(MotionEvent secondTap) {
+ if (mProcessingShiftDoubleTapEvent
+ && secondTap.getAction() == MotionEvent.ACTION_DOWN) {
+ final MotionEvent secondDown = secondTap;
+ final int pointerIndex = secondDown.getActionIndex();
+ final int id = secondDown.getPointerId(pointerIndex);
+ final PointerTracker tracker = getPointerTracker(id);
+ // If the second down event is also on shift key.
+ if (tracker.isOnShiftKey((int) secondDown.getX(), (int) secondDown.getY())) {
+ // Detected a double tap on shift key. If we are in the ignoring double tap
+ // mode, it means we have already turned off caps lock in
+ // {@link KeyboardSwitcher#onReleaseShift} .
+ final boolean ignoringDoubleTap = mKeyTimerHandler.isIgnoringDoubleTap();
+ if (!ignoringDoubleTap)
+ onDoubleTapShiftKey(tracker);
+ return true;
+ }
+ // Otherwise these events should not be handled as double tap.
+ mProcessingShiftDoubleTapEvent = false;
+ }
+ return mProcessingShiftDoubleTapEvent;
+ }
+ }
public LatinKeyboardView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, R.attr.keyboardViewStyle);
}
public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+
+ mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
+
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
+ mVerticalCorrection = a.getDimensionPixelOffset(
+ R.styleable.KeyboardView_verticalCorrection, 0);
+ mPopupLayout = a.getResourceId(R.styleable.KeyboardView_popupLayout, 0);
+ a.recycle();
+
+ final Resources res = getResources();
+ final float keyHysteresisDistance = res.getDimension(R.dimen.key_hysteresis_distance);
+ mKeyDetector = new KeyDetector(keyHysteresisDistance);
+
+ final boolean ignoreMultitouch = true;
+ mGestureDetector = new GestureDetector(
+ getContext(), new DoubleTapListener(), null, ignoreMultitouch);
+ mGestureDetector.setIsLongpressEnabled(false);
+
+ mHasDistinctMultitouch = context.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
+ mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
+
+ PointerTracker.init(mHasDistinctMultitouch, getContext());
+ }
+
+ public void startIgnoringDoubleTap() {
+ if (ENABLE_CAPSLOCK_BY_DOUBLETAP)
+ mKeyTimerHandler.startIgnoringDoubleTap();
+ }
+
+ public void setKeyboardActionListener(KeyboardActionListener listener) {
+ mKeyboardActionListener = listener;
+ PointerTracker.setKeyboardActionListener(listener);
+ }
+
+ /**
+ * Returns the {@link KeyboardActionListener} object.
+ * @return the listener attached to this keyboard
+ */
+ @Override
+ public KeyboardActionListener getKeyboardActionListener() {
+ return mKeyboardActionListener;
+ }
+
+ @Override
+ public KeyDetector getKeyDetector() {
+ return mKeyDetector;
+ }
+
+ @Override
+ public DrawingProxy getDrawingProxy() {
+ return this;
+ }
+
+ @Override
+ public TimerProxy getTimerProxy() {
+ return mKeyTimerHandler;
}
@Override
- public void setKeyboard(Keyboard newKeyboard) {
- super.setKeyboard(newKeyboard);
- // One-seventh of the keyboard width seems like a reasonable threshold
- final int jumpThreshold = newKeyboard.mOccupiedWidth / 7;
- mJumpThresholdSquare = jumpThreshold * jumpThreshold;
+ public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+ final Keyboard keyboard = getKeyboard();
+ if (keyboard instanceof LatinKeyboard) {
+ final LatinKeyboard latinKeyboard = (LatinKeyboard)keyboard;
+ if (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard()) {
+ // Phone and number keyboard never shows popup preview.
+ super.setKeyPreviewPopupEnabled(false, delay);
+ return;
+ }
+ }
+ super.setKeyPreviewPopupEnabled(previewEnabled, delay);
+ }
+
+ /**
+ * Attaches a keyboard to this view. The keyboard can be switched at any time and the
+ * view will re-layout itself to accommodate the keyboard.
+ * @see Keyboard
+ * @see #getKeyboard()
+ * @param keyboard the keyboard to display in this view
+ */
+ @Override
+ public void setKeyboard(Keyboard keyboard) {
+ // Remove any pending messages, except dismissing preview
+ mKeyTimerHandler.cancelKeyTimers();
+ super.setKeyboard(keyboard);
+ mKeyDetector.setKeyboard(
+ keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
+ mKeyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
+ PointerTracker.setKeyDetector(mKeyDetector);
+ mTouchScreenRegulator.setKeyboard(keyboard);
+ mPopupPanelCache.clear();
}
/**
- * This function checks to see if we need to handle any sudden jumps in the pointer location
- * that could be due to a multi-touch being treated as a move by the firmware or hardware.
- * Once a sudden jump is detected, all subsequent move events are discarded
- * until an UP is received.<P>
- * When a sudden jump is detected, an UP event is simulated at the last position and when
- * the sudden moves subside, a DOWN event is simulated for the second key.
- * @param me the motion event
- * @return true if the event was consumed, so that it doesn't continue to be handled by
- * {@link LatinKeyboardBaseView}.
+ * Returns whether the device has distinct multi-touch panel.
+ * @return true if the device has distinct multi-touch panel.
*/
- private boolean handleSuddenJump(MotionEvent me) {
- // If device has distinct multi touch panel, there is no need to check sudden jump.
- if (hasDistinctMultitouch())
+ public boolean hasDistinctMultitouch() {
+ return mHasDistinctMultitouch;
+ }
+
+ /**
+ * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key
+ * codes for adjacent keys. When disabled, only the primary key code will be
+ * reported.
+ * @param enabled whether or not the proximity correction is enabled
+ */
+ public void setProximityCorrectionEnabled(boolean enabled) {
+ mKeyDetector.setProximityCorrectionEnabled(enabled);
+ }
+
+ /**
+ * Returns true if proximity correction is enabled.
+ */
+ public boolean isProximityCorrectionEnabled() {
+ return mKeyDetector.isProximityCorrectionEnabled();
+ }
+
+ @Override
+ public void cancelAllMessages() {
+ mKeyTimerHandler.cancelAllMessages();
+ super.cancelAllMessages();
+ }
+
+ private boolean openMiniKeyboardIfRequired(int keyIndex, PointerTracker tracker) {
+ // Check if we have a popup layout specified first.
+ if (mPopupLayout == 0) {
+ return false;
+ }
+
+ // Check if we are already displaying popup panel.
+ if (mPopupPanel != null)
return false;
- final int action = me.getAction();
- final int x = (int) me.getX();
- final int y = (int) me.getY();
- boolean result = false;
-
- // Real multi-touch event? Stop looking for sudden jumps
- if (me.getPointerCount() > 1) {
- mDisableDisambiguation = true;
- }
- if (mDisableDisambiguation) {
- // If UP, reset the multi-touch flag
- if (action == MotionEvent.ACTION_UP) mDisableDisambiguation = false;
+ final Key parentKey = tracker.getKey(keyIndex);
+ if (parentKey == null)
return false;
+ return onLongPress(parentKey, tracker);
+ }
+
+ private void onDoubleTapShiftKey(@SuppressWarnings("unused") PointerTracker tracker) {
+ // When shift key is double tapped, the first tap is correctly processed as usual tap. And
+ // the second tap is treated as this double tap event, so that we need not mark tracker
+ // calling setAlreadyProcessed() nor remove the tracker from mPointerQueue.
+ mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
+ }
+
+ // This default implementation returns a popup mini keyboard panel.
+ protected PopupPanel onCreatePopupPanel(Key parentKey) {
+ if (parentKey.mPopupCharacters == null)
+ return null;
+
+ final View container = LayoutInflater.from(getContext()).inflate(mPopupLayout, null);
+ if (container == null)
+ throw new NullPointerException();
+
+ final PopupMiniKeyboardView miniKeyboardView =
+ (PopupMiniKeyboardView)container.findViewById(R.id.mini_keyboard_view);
+ final Keyboard parentKeyboard = getKeyboard();
+ final Keyboard miniKeyboard = new MiniKeyboard.Builder(
+ this, parentKeyboard.mPopupKeyboardResId, parentKey, parentKeyboard).build();
+ miniKeyboardView.setKeyboard(miniKeyboard);
+
+ container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+
+ return miniKeyboardView;
+ }
+
+ @Override
+ protected boolean needsToDimKeyboard() {
+ return mPopupPanel != null;
+ }
+
+ public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboard oldKeyboard) {
+ final Keyboard keyboard = getKeyboard();
+ // We should not set text fade factor to the keyboard which does not display the language on
+ // its spacebar.
+ if (keyboard instanceof LatinKeyboard && keyboard == oldKeyboard) {
+ ((LatinKeyboard)keyboard).setSpacebarTextFadeFactor(fadeFactor, this);
+ }
+ }
+
+ /**
+ * Called when a key is long pressed. By default this will open mini keyboard associated
+ * with this key.
+ * @param parentKey the key that was long pressed
+ * @param tracker the pointer tracker which pressed the parent key
+ * @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 parentKey, PointerTracker tracker) {
+ final int primaryCode = parentKey.mCode;
+ final Keyboard keyboard = getKeyboard();
+ if (keyboard instanceof LatinKeyboard) {
+ final LatinKeyboard latinKeyboard = (LatinKeyboard) keyboard;
+ if (primaryCode == Keyboard.CODE_DIGIT0 && latinKeyboard.isPhoneKeyboard()) {
+ tracker.onLongPressed();
+ // Long pressing on 0 in phone number keypad gives you a '+'.
+ return invokeOnKey(Keyboard.CODE_PLUS);
+ }
+ if (primaryCode == Keyboard.CODE_SHIFT && latinKeyboard.isAlphaKeyboard()) {
+ tracker.onLongPressed();
+ return invokeOnKey(Keyboard.CODE_CAPSLOCK);
+ }
+ }
+ if (primaryCode == Keyboard.CODE_SETTINGS || primaryCode == Keyboard.CODE_SPACE) {
+ // Both long pressing settings key and space key invoke IME switcher dialog.
+ if (getKeyboardActionListener().onCustomRequest(
+ LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
+ tracker.onLongPressed();
+ return true;
+ } else {
+ return openPopupPanel(parentKey, tracker);
+ }
+ } else {
+ return openPopupPanel(parentKey, tracker);
+ }
+ }
+
+ private boolean invokeOnKey(int primaryCode) {
+ getKeyboardActionListener().onCodeInput(primaryCode, null,
+ KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
+ KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
+ return true;
+ }
+
+ private boolean openPopupPanel(Key parentKey, PointerTracker tracker) {
+ PopupPanel popupPanel = mPopupPanelCache.get(parentKey);
+ if (popupPanel == null) {
+ popupPanel = onCreatePopupPanel(parentKey);
+ if (popupPanel == null)
+ return false;
+ mPopupPanelCache.put(parentKey, popupPanel);
+ }
+ if (mPopupWindow == null) {
+ mPopupWindow = new PopupWindow(getContext());
+ mPopupWindow.setBackgroundDrawable(null);
+ mPopupWindow.setAnimationStyle(R.style.PopupMiniKeyboardAnimation);
+ // Allow popup window to be drawn off the screen.
+ mPopupWindow.setClippingEnabled(false);
+ }
+ mPopupPanel = popupPanel;
+ mPopupPanelPointerTrackerId = tracker.mPointerId;
+
+ popupPanel.showPopupPanel(this, parentKey, tracker, mPopupWindow);
+ final int translatedX = popupPanel.translateX(tracker.getLastX());
+ final int translatedY = popupPanel.translateY(tracker.getLastY());
+ tracker.onShowPopupPanel(translatedX, translatedY, SystemClock.uptimeMillis(), popupPanel);
+
+ invalidateAllKeys();
+ return true;
+ }
+
+ private PointerTracker getPointerTracker(final int id) {
+ return PointerTracker.getPointerTracker(id, this);
+ }
+
+ public boolean isInSlidingKeyInput() {
+ if (mPopupPanel != null) {
+ return true;
+ } else {
+ return PointerTracker.isAnyInSlidingKeyInput();
+ }
+ }
+
+ public int getPointerCount() {
+ return mOldPointerCount;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent me) {
+ return mTouchScreenRegulator.onTouchEvent(me);
+ }
+
+ @Override
+ public boolean processMotionEvent(MotionEvent me) {
+ final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
+ final int action = me.getActionMasked();
+ final int pointerCount = me.getPointerCount();
+ final int oldPointerCount = mOldPointerCount;
+ mOldPointerCount = pointerCount;
+
+ // TODO: cleanup this code into a multi-touch to single-touch event converter class?
+ // If the device does not have distinct multi-touch support panel, ignore all multi-touch
+ // events except a transition from/to single-touch.
+ if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
+ return true;
+ }
+
+ // Gesture detector must be enabled only when mini-keyboard is not on the screen.
+ if (mPopupPanel == null && mGestureDetector != null
+ && mGestureDetector.onTouchEvent(me)) {
+ PointerTracker.dismissAllKeyPreviews();
+ mKeyTimerHandler.cancelKeyTimers();
+ return true;
+ }
+
+ final long eventTime = me.getEventTime();
+ final int index = me.getActionIndex();
+ final int id = me.getPointerId(index);
+ final int x, y;
+ if (mPopupPanel != null && id == mPopupPanelPointerTrackerId) {
+ x = mPopupPanel.translateX((int)me.getX(index));
+ y = mPopupPanel.translateY((int)me.getY(index));
+ } else {
+ x = (int)me.getX(index);
+ y = (int)me.getY(index);
+ }
+
+ if (mKeyTimerHandler.isInKeyRepeat()) {
+ final PointerTracker tracker = getPointerTracker(id);
+ // Key repeating timer will be canceled if 2 or more keys are in action, and current
+ // event (UP or DOWN) is non-modifier key.
+ if (pointerCount > 1 && !tracker.isModifier()) {
+ mKeyTimerHandler.cancelKeyRepeatTimer();
+ }
+ // Up event will pass through.
+ }
+
+ // TODO: cleanup this code into a multi-touch to single-touch event converter class?
+ // Translate mutli-touch event to single-touch events on the device that has no distinct
+ // multi-touch panel.
+ if (nonDistinctMultitouch) {
+ // Use only main (id=0) pointer tracker.
+ PointerTracker tracker = getPointerTracker(0);
+ if (pointerCount == 1 && oldPointerCount == 2) {
+ // Multi-touch to single touch transition.
+ // Send a down event for the latest pointer if the key is different from the
+ // previous key.
+ final int newKeyIndex = tracker.getKeyIndexOn(x, y);
+ if (mOldKeyIndex != newKeyIndex) {
+ tracker.onDownEvent(x, y, eventTime, this);
+ if (action == MotionEvent.ACTION_UP)
+ tracker.onUpEvent(x, y, eventTime);
+ }
+ } else if (pointerCount == 2 && oldPointerCount == 1) {
+ // Single-touch to multi-touch transition.
+ // Send an up event for the last pointer.
+ final int lastX = tracker.getLastX();
+ final int lastY = tracker.getLastY();
+ mOldKeyIndex = tracker.getKeyIndexOn(lastX, lastY);
+ tracker.onUpEvent(lastX, lastY, eventTime);
+ } else if (pointerCount == 1 && oldPointerCount == 1) {
+ processMotionEvent(tracker, action, x, y, eventTime, this);
+ } else {
+ Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
+ + " (old " + oldPointerCount + ")");
+ }
+ return true;
+ }
+
+ if (action == MotionEvent.ACTION_MOVE) {
+ for (int i = 0; i < pointerCount; i++) {
+ final PointerTracker tracker = getPointerTracker(me.getPointerId(i));
+ final int px, py;
+ if (mPopupPanel != null && tracker.mPointerId == mPopupPanelPointerTrackerId) {
+ px = mPopupPanel.translateX((int)me.getX(i));
+ py = mPopupPanel.translateY((int)me.getY(i));
+ } else {
+ px = (int)me.getX(i);
+ py = (int)me.getY(i);
+ }
+ tracker.onMoveEvent(px, py, eventTime);
+ }
+ } else {
+ processMotionEvent(getPointerTracker(id), action, x, y, eventTime, this);
}
+ return true;
+ }
+
+ private static void processMotionEvent(PointerTracker tracker, int action, int x, int y,
+ long eventTime, PointerTracker.KeyEventHandler handler) {
switch (action) {
case MotionEvent.ACTION_DOWN:
- // Reset the "session"
- mDroppingEvents = false;
- mDisableDisambiguation = false;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ tracker.onDownEvent(x, y, eventTime, handler);
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ tracker.onUpEvent(x, y, eventTime);
break;
case MotionEvent.ACTION_MOVE:
- // Is this a big jump?
- final int distanceSquare = (mLastX - x) * (mLastX - x) + (mLastY - y) * (mLastY - y);
- // Check the distance.
- if (distanceSquare > mJumpThresholdSquare) {
- // If we're not yet dropping events, start dropping and send an UP event
- if (!mDroppingEvents) {
- mDroppingEvents = true;
- // Send an up event
- MotionEvent translated = MotionEvent.obtain(
- me.getEventTime(), me.getEventTime(),
- MotionEvent.ACTION_UP,
- mLastX, mLastY, me.getMetaState());
- super.onTouchEvent(translated);
- translated.recycle();
- }
- result = true;
- } else if (mDroppingEvents) {
- // If moves are small and we're already dropping events, continue dropping
- result = true;
- }
+ tracker.onMoveEvent(x, y, eventTime);
break;
- case MotionEvent.ACTION_UP:
- if (mDroppingEvents) {
- // Send a down event first, as we dropped a bunch of sudden jumps and assume that
- // the user is releasing the touch on the second key.
- MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
- MotionEvent.ACTION_DOWN,
- x, y, me.getMetaState());
- super.onTouchEvent(translated);
- translated.recycle();
- mDroppingEvents = false;
- // Let the up event get processed as well, result = false
- }
+ case MotionEvent.ACTION_CANCEL:
+ tracker.onCancelEvent(x, y, eventTime);
break;
}
- // Track the previous coordinate
- mLastX = x;
- mLastY = y;
- return result;
}
@Override
- public boolean onTouchEvent(MotionEvent me) {
- if (getKeyboard() == null) return true;
+ public void closing() {
+ super.closing();
+ dismissPopupPanel();
+ mPopupPanelCache.clear();
+ }
- // If there was a sudden jump, return without processing the actual motion event.
- if (handleSuddenJump(me)) {
- if (DEBUG_MODE)
- Log.w(TAG, "onTouchEvent: ignore sudden jump " + me);
+ @Override
+ public boolean dismissPopupPanel() {
+ if (mPopupWindow != null && mPopupWindow.isShowing()) {
+ mPopupWindow.dismiss();
+ mPopupPanel = null;
+ mPopupPanelPointerTrackerId = -1;
+ invalidateAllKeys();
return true;
}
+ return false;
+ }
+
+ public boolean handleBack() {
+ return dismissPopupPanel();
+ }
+
+ @Override
+ public void draw(Canvas c) {
+ Utils.GCUtils.getInstance().reset();
+ boolean tryGC = true;
+ for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
+ try {
+ super.draw(c);
+ tryGC = false;
+ } catch (OutOfMemoryError e) {
+ tryGC = Utils.GCUtils.getInstance().tryGCOrWait("LatinKeyboardView", e);
+ }
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ // Token is available from here.
+ VoiceProxy.getInstance().onAttachedToWindow();
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ // Drop non-hover touch events when touch exploration is enabled.
+ if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+ return false;
+ }
+
+ return super.dispatchTouchEvent(event);
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+ final PointerTracker tracker = getPointerTracker(0);
+ return AccessibleKeyboardViewProxy.getInstance().dispatchPopulateAccessibilityEvent(
+ event, tracker) || super.dispatchPopulateAccessibilityEvent(event);
+ }
+
+ return super.dispatchPopulateAccessibilityEvent(event);
+ }
+
+ /**
+ * Receives hover events from the input framework. This method overrides
+ * View.dispatchHoverEvent(MotionEvent) on SDK version ICS or higher. On
+ * lower SDK versions, this method is never called.
+ *
+ * @param event The motion event to be dispatched.
+ * @return {@code true} if the event was handled by the view, {@code false}
+ * otherwise
+ */
+ public boolean dispatchHoverEvent(MotionEvent event) {
+ if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+ final PointerTracker tracker = getPointerTracker(0);
+ return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
+ }
- return super.onTouchEvent(me);
+ // Reflection doesn't support calling superclass methods.
+ return false;
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
index 08e7d7e19..17c253963 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
@@ -16,12 +16,18 @@
package com.android.inputmethod.keyboard;
-import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder.MiniKeyboardParams;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.keyboard.internal.PopupCharactersParser;
+import com.android.inputmethod.latin.R;
public class MiniKeyboard extends Keyboard {
private final int mDefaultKeyCoordX;
- public MiniKeyboard(MiniKeyboardParams params) {
+ private MiniKeyboard(Builder.MiniKeyboardParams params) {
super(params);
mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2;
}
@@ -29,4 +35,244 @@ public class MiniKeyboard extends Keyboard {
public int getDefaultCoordX() {
return mDefaultKeyCoordX;
}
+
+ public static class Builder extends KeyboardBuilder<Builder.MiniKeyboardParams> {
+ private final CharSequence[] mPopupCharacters;
+
+ public static class MiniKeyboardParams extends KeyboardParams {
+ /* package */int mTopRowAdjustment;
+ public int mNumRows;
+ public int mNumColumns;
+ public int mLeftKeys;
+ public int mRightKeys; // includes default key.
+
+ public MiniKeyboardParams() {
+ super();
+ }
+
+ /* package for test */MiniKeyboardParams(int numKeys, int maxColumns, int keyWidth,
+ int rowHeight, int coordXInParent, int parentKeyboardWidth) {
+ super();
+ setParameters(numKeys, maxColumns, keyWidth, rowHeight, coordXInParent,
+ parentKeyboardWidth);
+ }
+
+ /**
+ * Set keyboard parameters of mini keyboard.
+ *
+ * @param numKeys number of keys in this mini keyboard.
+ * @param maxColumns number of maximum columns of this mini keyboard.
+ * @param keyWidth mini keyboard key width in pixel, including horizontal gap.
+ * @param rowHeight mini keyboard row height in pixel, including vertical gap.
+ * @param coordXInParent coordinate x of the popup key in parent keyboard.
+ * @param parentKeyboardWidth parent keyboard width in pixel.
+ */
+ public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight,
+ int coordXInParent, int parentKeyboardWidth) {
+ if (parentKeyboardWidth / keyWidth < maxColumns) {
+ throw new IllegalArgumentException(
+ "Keyboard is too small to hold mini keyboard: " + parentKeyboardWidth
+ + " " + keyWidth + " " + maxColumns);
+ }
+ mDefaultKeyWidth = keyWidth;
+ mDefaultRowHeight = rowHeight;
+
+ final int numRows = (numKeys + maxColumns - 1) / maxColumns;
+ mNumRows = numRows;
+ final int numColumns = getOptimizedColumns(numKeys, maxColumns);
+ mNumColumns = numColumns;
+
+ final int numLeftKeys = (numColumns - 1) / 2;
+ final int numRightKeys = numColumns - numLeftKeys; // including default key.
+ final int maxLeftKeys = coordXInParent / keyWidth;
+ final int maxRightKeys = Math.max(1, (parentKeyboardWidth - coordXInParent)
+ / keyWidth);
+ int leftKeys, rightKeys;
+ if (numLeftKeys > maxLeftKeys) {
+ leftKeys = maxLeftKeys;
+ rightKeys = numColumns - maxLeftKeys;
+ } else if (numRightKeys > maxRightKeys) {
+ leftKeys = numColumns - maxRightKeys;
+ rightKeys = maxRightKeys;
+ } else {
+ leftKeys = numLeftKeys;
+ rightKeys = numRightKeys;
+ }
+ // Shift right if the left edge of mini keyboard is on the edge of parent keyboard
+ // unless the parent key is on the left edge.
+ if (leftKeys * keyWidth >= coordXInParent && leftKeys > 0) {
+ leftKeys--;
+ rightKeys++;
+ }
+ // Shift left if the right edge of mini keyboard is on the edge of parent keyboard
+ // unless the parent key is on the right edge.
+ if (rightKeys * keyWidth + coordXInParent >= parentKeyboardWidth && rightKeys > 1) {
+ leftKeys++;
+ rightKeys--;
+ }
+ mLeftKeys = leftKeys;
+ mRightKeys = rightKeys;
+
+ // Centering of the top row.
+ final boolean onEdge = (leftKeys == 0 || rightKeys == 1);
+ if (numRows < 2 || onEdge || getTopRowEmptySlots(numKeys, numColumns) % 2 == 0) {
+ mTopRowAdjustment = 0;
+ } else if (mLeftKeys < mRightKeys - 1) {
+ mTopRowAdjustment = 1;
+ } else {
+ mTopRowAdjustment = -1;
+ }
+
+ mWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth;
+ mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
+ }
+
+ // Return key position according to column count (0 is default).
+ /* package */int getColumnPos(int n) {
+ final int col = n % mNumColumns;
+ if (col == 0) {
+ // default position.
+ return 0;
+ }
+ int pos = 0;
+ int right = 1; // include default position key.
+ int left = 0;
+ int i = 0;
+ while (true) {
+ // Assign right key if available.
+ if (right < mRightKeys) {
+ pos = right;
+ right++;
+ i++;
+ }
+ if (i >= col)
+ break;
+ // Assign left key if available.
+ if (left < mLeftKeys) {
+ left++;
+ pos = -left;
+ i++;
+ }
+ if (i >= col)
+ break;
+ }
+ return pos;
+ }
+
+ private static int getTopRowEmptySlots(int numKeys, int numColumns) {
+ final int remainingKeys = numKeys % numColumns;
+ if (remainingKeys == 0) {
+ return 0;
+ } else {
+ return numColumns - remainingKeys;
+ }
+ }
+
+ private int getOptimizedColumns(int numKeys, int maxColumns) {
+ int numColumns = Math.min(numKeys, maxColumns);
+ while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
+ numColumns--;
+ }
+ return numColumns;
+ }
+
+ public int getDefaultKeyCoordX() {
+ return mLeftKeys * mDefaultKeyWidth;
+ }
+
+ public int getX(int n, int row) {
+ final int x = getColumnPos(n) * mDefaultKeyWidth + getDefaultKeyCoordX();
+ if (isTopRow(row)) {
+ return x + mTopRowAdjustment * (mDefaultKeyWidth / 2);
+ }
+ return x;
+ }
+
+ public int getY(int row) {
+ return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
+ }
+
+ public int getRowFlags(int row) {
+ int rowFlags = 0;
+ if (row == 0)
+ rowFlags |= Keyboard.EDGE_TOP;
+ if (isTopRow(row))
+ rowFlags |= Keyboard.EDGE_BOTTOM;
+ return rowFlags;
+ }
+
+ private boolean isTopRow(int rowCount) {
+ return rowCount == mNumRows - 1;
+ }
+ }
+
+ public Builder(KeyboardView view, int xmlId, Key parentKey, Keyboard parentKeyboard) {
+ super(view.getContext(), new MiniKeyboardParams());
+ load(parentKeyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId));
+
+ // HACK: Current mini keyboard design totally relies on the 9-patch
+ // padding about horizontal
+ // and vertical key spacing. To keep the visual of mini keyboard as
+ // is, these hacks are
+ // needed to keep having the same horizontal and vertical key
+ // spacing.
+ mParams.mHorizontalGap = 0;
+ mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
+ // TODO: When we have correctly padded key background 9-patch
+ // drawables for mini keyboard,
+ // revert the above hacks and uncomment the following lines.
+ // mParams.mHorizontalGap = parentKeyboard.mHorizontalGap;
+ // mParams.mVerticalGap = parentKeyboard.mVerticalGap;
+
+ mParams.mIsRtlKeyboard = parentKeyboard.mIsRtlKeyboard;
+ mPopupCharacters = parentKey.mPopupCharacters;
+
+ final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, mParams.mDefaultKeyWidth);
+ mParams.setParameters(mPopupCharacters.length, parentKey.mMaxPopupColumn, keyWidth,
+ parentKeyboard.mDefaultRowHeight, parentKey.mX
+ + (mParams.mDefaultKeyWidth - keyWidth) / 2, view.getMeasuredWidth());
+ }
+
+ private static int getMaxKeyWidth(KeyboardView view, CharSequence[] popupCharacters,
+ int minKeyWidth) {
+ Paint paint = null;
+ Rect bounds = null;
+ int maxWidth = 0;
+ for (CharSequence popupSpec : popupCharacters) {
+ final CharSequence label = PopupCharactersParser.getLabel(popupSpec.toString());
+ // If the label is single letter, minKeyWidth is enough to hold
+ // the label.
+ if (label != null && label.length() > 1) {
+ if (paint == null) {
+ paint = new Paint();
+ paint.setAntiAlias(true);
+ }
+ final int labelSize = view.getDefaultLabelSizeAndSetPaint(paint);
+ paint.setTextSize(labelSize);
+ if (bounds == null)
+ bounds = new Rect();
+ paint.getTextBounds(label.toString(), 0, label.length(), bounds);
+ if (maxWidth < bounds.width())
+ maxWidth = bounds.width();
+ }
+ }
+ final int horizontalPadding = (int) view.getContext().getResources()
+ .getDimension(R.dimen.mini_keyboard_key_horizontal_padding);
+ return Math.max(minKeyWidth, maxWidth + horizontalPadding);
+ }
+
+ @Override
+ public MiniKeyboard build() {
+ final MiniKeyboardParams params = mParams;
+ for (int n = 0; n < mPopupCharacters.length; n++) {
+ final CharSequence label = mPopupCharacters[n];
+ final int row = n / params.mNumColumns;
+ final Key key = new Key(mResources, params, label, params.getX(n, row),
+ params.getY(row), params.mDefaultKeyWidth, params.mDefaultRowHeight,
+ params.getRowFlags(row));
+ params.onAddKey(key);
+ }
+ return new MiniKeyboard(params);
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
deleted file mode 100644
index 84bd44c30..000000000
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.keyboard;
-
-import java.util.List;
-
-public class MiniKeyboardKeyDetector extends KeyDetector {
- private final int mSlideAllowanceSquare;
- private final int mSlideAllowanceSquareTop;
-
- public MiniKeyboardKeyDetector(float slideAllowance) {
- super(/* keyHysteresisDistance */0);
- mSlideAllowanceSquare = (int)(slideAllowance * slideAllowance);
- // Top slide allowance is slightly longer (sqrt(2) times) than other edges.
- mSlideAllowanceSquareTop = mSlideAllowanceSquare * 2;
- }
-
- @Override
- protected int getMaxNearbyKeys() {
- // No nearby key will be returned.
- return 1;
- }
-
- @Override
- public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
- final List<Key> keys = getKeyboard().mKeys;
- final int touchX = getTouchX(x);
- final int touchY = getTouchY(y);
-
- int nearestIndex = NOT_A_KEY;
- int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
- final int keyCount = keys.size();
- for (int index = 0; index < keyCount; index++) {
- final int dist = keys.get(index).squaredDistanceToEdge(touchX, touchY);
- if (dist < nearestDist) {
- nearestIndex = index;
- nearestDist = dist;
- }
- }
-
- if (allCodes != null && nearestIndex != NOT_A_KEY)
- allCodes[0] = keys.get(nearestIndex).mCode;
- return nearestIndex;
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 1f8119a0f..d33cb442b 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -438,9 +438,9 @@ public class PointerTracker {
private void onDownEventInternal(int x, int y, long eventTime) {
int keyIndex = onDownKey(x, y, eventTime);
// Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
- // from modifier key, or 3) this pointer is on mini-keyboard.
+ // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
mIsAllowedSlidingKeyInput = sConfigSlidingKeyInputEnabled || isModifierInternal(keyIndex)
- || mKeyDetector instanceof MiniKeyboardKeyDetector;
+ || mKeyDetector.alwaysAllowsSlidingInput();
mKeyboardLayoutHasBeenChanged = false;
mKeyAlreadyProcessed = false;
mIsRepeatableKey = false;
diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
index fb932e3e8..2396222bc 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
@@ -28,6 +28,8 @@ import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
import com.android.inputmethod.latin.R;
+import java.util.List;
+
/**
* A view that renders a virtual {@link MiniKeyboard}. It handles rendering of keys and detecting
* key presses and touch movements.
@@ -39,10 +41,55 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
private final KeyDetector mKeyDetector;
private final int mVerticalCorrection;
- private LatinKeyboardBaseView mParentKeyboardView;
+ private LatinKeyboardView mParentKeyboardView;
private int mOriginX;
private int mOriginY;
+ private static class MiniKeyboardKeyDetector extends KeyDetector {
+ private final int mSlideAllowanceSquare;
+ private final int mSlideAllowanceSquareTop;
+
+ public MiniKeyboardKeyDetector(float slideAllowance) {
+ super(/* keyHysteresisDistance */0);
+ mSlideAllowanceSquare = (int)(slideAllowance * slideAllowance);
+ // Top slide allowance is slightly longer (sqrt(2) times) than other edges.
+ mSlideAllowanceSquareTop = mSlideAllowanceSquare * 2;
+ }
+
+ @Override
+ public boolean alwaysAllowsSlidingInput() {
+ return true;
+ }
+
+ @Override
+ protected int getMaxNearbyKeys() {
+ // No nearby key will be returned.
+ return 1;
+ }
+
+ @Override
+ public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
+ final List<Key> keys = getKeyboard().mKeys;
+ final int touchX = getTouchX(x);
+ final int touchY = getTouchY(y);
+
+ int nearestIndex = NOT_A_KEY;
+ int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
+ final int keyCount = keys.size();
+ for (int index = 0; index < keyCount; index++) {
+ final int dist = keys.get(index).squaredDistanceToEdge(touchX, touchY);
+ if (dist < nearestDist) {
+ nearestIndex = index;
+ nearestDist = dist;
+ }
+ }
+
+ if (allCodes != null && nearestIndex != NOT_A_KEY)
+ allCodes[0] = keys.get(nearestIndex).mCode;
+ return nearestIndex;
+ }
+ }
+
private static final TimerProxy EMPTY_TIMER_PROXY = new TimerProxy() {
@Override
public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {}
@@ -146,11 +193,6 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
}
@Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- // Do nothing for the mini keyboard.
- }
-
- @Override
public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
// Mini keyboard needs no pop-up key preview displayed, so we pass always false with a
// delay of 0. The delay does not matter actually since the popup is not shown anyway.
@@ -158,7 +200,7 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
}
@Override
- public void showPopupPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
+ public void showPopupPanel(LatinKeyboardView parentKeyboardView, Key parentKey,
PointerTracker tracker, PopupWindow window) {
mParentKeyboardView = parentKeyboardView;
final View container = (View)getParent();
diff --git a/java/src/com/android/inputmethod/keyboard/PopupPanel.java b/java/src/com/android/inputmethod/keyboard/PopupPanel.java
index dc526e74f..db637c50b 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupPanel.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupPanel.java
@@ -26,7 +26,7 @@ public interface PopupPanel extends PointerTracker.KeyEventHandler {
* @param tracker the pointer tracker that pressesd the parent key
* @param window PopupWindow to be used to show this popup panel
*/
- public void showPopupPanel(LatinKeyboardBaseView parentKeyboardView, Key parentKey,
+ public void showPopupPanel(LatinKeyboardView parentKeyboardView, Key parentKey,
PointerTracker tracker, PopupWindow window);
/**
diff --git a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
new file mode 100644
index 000000000..c4251ccfc
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.R;
+
+public class SuddenJumpingTouchEventHandler {
+ private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName();
+ private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
+
+ public interface ProcessMotionEvent {
+ public boolean processMotionEvent(MotionEvent me);
+ }
+
+ private final ProcessMotionEvent mView;
+ private final boolean mNeedsSuddenJumpingHack;
+
+ /** Whether we've started dropping move events because we found a big jump */
+ private boolean mDroppingEvents;
+ /**
+ * Whether multi-touch disambiguation needs to be disabled if a real multi-touch event has
+ * occured
+ */
+ private boolean mDisableDisambiguation;
+ /** The distance threshold at which we start treating the touch session as a multi-touch */
+ private int mJumpThresholdSquare = Integer.MAX_VALUE;
+ private int mLastX;
+ private int mLastY;
+
+ public SuddenJumpingTouchEventHandler(Context context, ProcessMotionEvent view) {
+ mView = view;
+ final String[] deviceList = context.getResources().getStringArray(
+ R.array.sudden_jumping_touch_event_device_list);
+ mNeedsSuddenJumpingHack = needsSuddenJumpingHack(Build.DEVICE, deviceList);
+ }
+
+ private static boolean needsSuddenJumpingHack(String deviceName, String[] deviceList) {
+ for (String device : deviceList) {
+ if (device.equalsIgnoreCase(deviceName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setKeyboard(Keyboard newKeyboard) {
+ // One-seventh of the keyboard width seems like a reasonable threshold
+ final int jumpThreshold = newKeyboard.mOccupiedWidth / 7;
+ mJumpThresholdSquare = jumpThreshold * jumpThreshold;
+ }
+
+ /**
+ * This function checks to see if we need to handle any sudden jumps in the pointer location
+ * that could be due to a multi-touch being treated as a move by the firmware or hardware.
+ * Once a sudden jump is detected, all subsequent move events are discarded
+ * until an UP is received.<P>
+ * When a sudden jump is detected, an UP event is simulated at the last position and when
+ * the sudden moves subside, a DOWN event is simulated for the second key.
+ * @param me the motion event
+ * @return true if the event was consumed, so that it doesn't continue to be handled by
+ * {@link LatinKeyboardView}.
+ */
+ private boolean handleSuddenJumping(MotionEvent me) {
+ if (!mNeedsSuddenJumpingHack)
+ return false;
+ final int action = me.getAction();
+ final int x = (int) me.getX();
+ final int y = (int) me.getY();
+ boolean result = false;
+
+ // Real multi-touch event? Stop looking for sudden jumps
+ if (me.getPointerCount() > 1) {
+ mDisableDisambiguation = true;
+ }
+ if (mDisableDisambiguation) {
+ // If UP, reset the multi-touch flag
+ if (action == MotionEvent.ACTION_UP) mDisableDisambiguation = false;
+ return false;
+ }
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ // Reset the "session"
+ mDroppingEvents = false;
+ mDisableDisambiguation = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // Is this a big jump?
+ final int distanceSquare = (mLastX - x) * (mLastX - x) + (mLastY - y) * (mLastY - y);
+ // Check the distance.
+ if (distanceSquare > mJumpThresholdSquare) {
+ // If we're not yet dropping events, start dropping and send an UP event
+ if (!mDroppingEvents) {
+ mDroppingEvents = true;
+ // Send an up event
+ MotionEvent translated = MotionEvent.obtain(
+ me.getEventTime(), me.getEventTime(),
+ MotionEvent.ACTION_UP,
+ mLastX, mLastY, me.getMetaState());
+ mView.processMotionEvent(translated);
+ translated.recycle();
+ }
+ result = true;
+ } else if (mDroppingEvents) {
+ // If moves are small and we're already dropping events, continue dropping
+ result = true;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mDroppingEvents) {
+ // Send a down event first, as we dropped a bunch of sudden jumps and assume that
+ // the user is releasing the touch on the second key.
+ MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
+ MotionEvent.ACTION_DOWN,
+ x, y, me.getMetaState());
+ mView.processMotionEvent(translated);
+ translated.recycle();
+ mDroppingEvents = false;
+ // Let the up event get processed as well, result = false
+ }
+ break;
+ }
+ // Track the previous coordinate
+ mLastX = x;
+ mLastY = y;
+ return result;
+ }
+
+ public boolean onTouchEvent(MotionEvent me) {
+ // If there was a sudden jump, return without processing the actual motion event.
+ if (handleSuddenJumping(me)) {
+ if (DEBUG_MODE)
+ Log.w(TAG, "onTouchEvent: ignore sudden jump " + me);
+ return true;
+ }
+ return mView.processMotionEvent(me);
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
deleted file mode 100644
index 31a291cef..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.graphics.Paint;
-import android.graphics.Rect;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardView;
-import com.android.inputmethod.keyboard.MiniKeyboard;
-import com.android.inputmethod.latin.R;
-
-public class MiniKeyboardBuilder extends
- KeyboardBuilder<MiniKeyboardBuilder.MiniKeyboardParams> {
- private final CharSequence[] mPopupCharacters;
-
- public static class MiniKeyboardParams extends KeyboardParams {
- /* package */ int mTopRowAdjustment;
- public int mNumRows;
- public int mNumColumns;
- public int mLeftKeys;
- public int mRightKeys; // includes default key.
-
- public MiniKeyboardParams() {
- super();
- }
-
- /* package for test */ MiniKeyboardParams(int numKeys, int maxColumns, int keyWidth,
- int rowHeight, int coordXInParent, int parentKeyboardWidth) {
- super();
- setParameters(
- numKeys, maxColumns, keyWidth, rowHeight, coordXInParent, parentKeyboardWidth);
- }
-
- /**
- * Set keyboard parameters of mini keyboard.
- *
- * @param numKeys number of keys in this mini keyboard.
- * @param maxColumns number of maximum columns of this mini keyboard.
- * @param keyWidth mini keyboard key width in pixel, including horizontal gap.
- * @param rowHeight mini keyboard row height in pixel, including vertical gap.
- * @param coordXInParent coordinate x of the popup key in parent keyboard.
- * @param parentKeyboardWidth parent keyboard width in pixel.
- */
- public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight,
- int coordXInParent, int parentKeyboardWidth) {
- if (parentKeyboardWidth / keyWidth < maxColumns) {
- throw new IllegalArgumentException("Keyboard is too small to hold mini keyboard: "
- + parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
- }
- mDefaultKeyWidth = keyWidth;
- mDefaultRowHeight = rowHeight;
-
- final int numRows = (numKeys + maxColumns - 1) / maxColumns;
- mNumRows = numRows;
- final int numColumns = getOptimizedColumns(numKeys, maxColumns);
- mNumColumns = numColumns;
-
- final int numLeftKeys = (numColumns - 1) / 2;
- final int numRightKeys = numColumns - numLeftKeys; // including default key.
- final int maxLeftKeys = coordXInParent / keyWidth;
- final int maxRightKeys = Math.max(1, (parentKeyboardWidth - coordXInParent) / keyWidth);
- int leftKeys, rightKeys;
- if (numLeftKeys > maxLeftKeys) {
- leftKeys = maxLeftKeys;
- rightKeys = numColumns - maxLeftKeys;
- } else if (numRightKeys > maxRightKeys) {
- leftKeys = numColumns - maxRightKeys;
- rightKeys = maxRightKeys;
- } else {
- leftKeys = numLeftKeys;
- rightKeys = numRightKeys;
- }
- // Shift right if the left edge of mini keyboard is on the edge of parent keyboard
- // unless the parent key is on the left edge.
- if (leftKeys * keyWidth >= coordXInParent && leftKeys > 0) {
- leftKeys--;
- rightKeys++;
- }
- // Shift left if the right edge of mini keyboard is on the edge of parent keyboard
- // unless the parent key is on the right edge.
- if (rightKeys * keyWidth + coordXInParent >= parentKeyboardWidth && rightKeys > 1) {
- leftKeys++;
- rightKeys--;
- }
- mLeftKeys = leftKeys;
- mRightKeys = rightKeys;
-
- // Centering of the top row.
- final boolean onEdge = (leftKeys == 0 || rightKeys == 1);
- if (numRows < 2 || onEdge || getTopRowEmptySlots(numKeys, numColumns) % 2 == 0) {
- mTopRowAdjustment = 0;
- } else if (mLeftKeys < mRightKeys - 1) {
- mTopRowAdjustment = 1;
- } else {
- mTopRowAdjustment = -1;
- }
-
- mWidth = mOccupiedWidth = mNumColumns * mDefaultKeyWidth;
- mHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
- }
-
- // Return key position according to column count (0 is default).
- /* package */ int getColumnPos(int n) {
- final int col = n % mNumColumns;
- if (col == 0) {
- // default position.
- return 0;
- }
- int pos = 0;
- int right = 1; // include default position key.
- int left = 0;
- int i = 0;
- while (true) {
- // Assign right key if available.
- if (right < mRightKeys) {
- pos = right;
- right++;
- i++;
- }
- if (i >= col)
- break;
- // Assign left key if available.
- if (left < mLeftKeys) {
- left++;
- pos = -left;
- i++;
- }
- if (i >= col)
- break;
- }
- return pos;
- }
-
- private static int getTopRowEmptySlots(int numKeys, int numColumns) {
- final int remainingKeys = numKeys % numColumns;
- if (remainingKeys == 0) {
- return 0;
- } else {
- return numColumns - remainingKeys;
- }
- }
-
- private int getOptimizedColumns(int numKeys, int maxColumns) {
- int numColumns = Math.min(numKeys, maxColumns);
- while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
- numColumns--;
- }
- return numColumns;
- }
-
- public int getDefaultKeyCoordX() {
- return mLeftKeys * mDefaultKeyWidth;
- }
-
- public int getX(int n, int row) {
- final int x = getColumnPos(n) * mDefaultKeyWidth + getDefaultKeyCoordX();
- if (isTopRow(row)) {
- return x + mTopRowAdjustment * (mDefaultKeyWidth / 2);
- }
- return x;
- }
-
- public int getY(int row) {
- return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
- }
-
- public int getRowFlags(int row) {
- int rowFlags = 0;
- if (row == 0) rowFlags |= Keyboard.EDGE_TOP;
- if (isTopRow(row)) rowFlags |= Keyboard.EDGE_BOTTOM;
- return rowFlags;
- }
-
- private boolean isTopRow(int rowCount) {
- return rowCount == mNumRows - 1;
- }
- }
-
- public MiniKeyboardBuilder(KeyboardView view, int xmlId, Key parentKey,
- Keyboard parentKeyboard) {
- super(view.getContext(), new MiniKeyboardParams());
- load(parentKeyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId));
-
- // HACK: Current mini keyboard design totally relies on the 9-patch padding about horizontal
- // and vertical key spacing. To keep the visual of mini keyboard as is, these hacks are
- // needed to keep having the same horizontal and vertical key spacing.
- mParams.mHorizontalGap = 0;
- mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
- // TODO: When we have correctly padded key background 9-patch drawables for mini keyboard,
- // revert the above hacks and uncomment the following lines.
- //mParams.mHorizontalGap = parentKeyboard.mHorizontalGap;
- //mParams.mVerticalGap = parentKeyboard.mVerticalGap;
-
- mParams.mIsRtlKeyboard = parentKeyboard.mIsRtlKeyboard;
- mPopupCharacters = parentKey.mPopupCharacters;
-
- final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, mParams.mDefaultKeyWidth);
- mParams.setParameters(
- mPopupCharacters.length, parentKey.mMaxPopupColumn,
- keyWidth, parentKeyboard.mDefaultRowHeight,
- parentKey.mX + (mParams.mDefaultKeyWidth - keyWidth) / 2,
- view.getMeasuredWidth());
- }
-
- private static int getMaxKeyWidth(KeyboardView view, CharSequence[] popupCharacters,
- int minKeyWidth) {
- Paint paint = null;
- Rect bounds = null;
- int maxWidth = 0;
- for (CharSequence popupSpec : popupCharacters) {
- final CharSequence label = PopupCharactersParser.getLabel(popupSpec.toString());
- // If the label is single letter, minKeyWidth is enough to hold the label.
- if (label != null && label.length() > 1) {
- if (paint == null) {
- paint = new Paint();
- paint.setAntiAlias(true);
- }
- final int labelSize = view.getDefaultLabelSizeAndSetPaint(paint);
- paint.setTextSize(labelSize);
- if (bounds == null) bounds = new Rect();
- paint.getTextBounds(label.toString(), 0, label.length(), bounds);
- if (maxWidth < bounds.width())
- maxWidth = bounds.width();
- }
- }
- final int horizontalPadding = (int)view.getContext().getResources().getDimension(
- R.dimen.mini_keyboard_key_horizontal_padding);
- return Math.max(minKeyWidth, maxWidth + horizontalPadding);
- }
-
- @Override
- public MiniKeyboard build() {
- final MiniKeyboardParams params = mParams;
- for (int n = 0; n < mPopupCharacters.length; n++) {
- final CharSequence label = mPopupCharacters[n];
- final int row = n / params.mNumColumns;
- final Key key = new Key(mResources, params, label, params.getX(n, row), params.getY(row),
- params.mDefaultKeyWidth, params.mDefaultRowHeight, params.getRowFlags(row));
- params.onAddKey(key);
- }
- return new MiniKeyboard(params);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
index 032489e66..7c5abe32a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
@@ -196,13 +196,6 @@ public class PopupCharactersParser {
}
};
- public static final CodeFilter NON_ASCII_FILTER = new CodeFilter() {
- @Override
- public boolean shouldFilterOut(int code) {
- return code < 0x20 || code > 0x7e;
- }
- };
-
public static CharSequence[] filterOut(Resources res, CharSequence[] popupCharacters,
CodeFilter filter) {
if (popupCharacters == null || popupCharacters.length < 1) {
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index ffd204dac..9642151d7 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -64,24 +64,10 @@ public class DictionaryFactory {
}
}
- // null == dictList is not supposed to be possible, but better safe than sorry and it's
- // safer for future extension. In this case, rather than returning null, it should be safer
- // to return an empty DictionaryCollection.
- if (null == dictList) {
- return new DictionaryCollection();
- } else {
- if (dictList.isEmpty()) {
- // The list may be empty if no dictionaries have been added. The getter should not
- // return an empty list, but if it does we end up here. Likewise, if the files
- // we found could not be opened by the native code for any reason (format mismatch,
- // file too big to fit in memory, etc) then we could have an empty list. In this
- // case we want to fall back on the resource.
- return new DictionaryCollection(createBinaryDictionary(context, fallbackResId,
- locale));
- } else {
- return new DictionaryCollection(dictList);
- }
- }
+ // If the list is empty, that means we should not use any dictionary (for example, the user
+ // explicitly disabled the main dictionary), so the following is okay. dictList is never
+ // null, but if for some reason it is, DictionaryCollection handles it gracefully.
+ return new DictionaryCollection(dictList);
}
/**
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index afbdd36a9..394414d03 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -113,7 +113,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// Key events coming any faster than this are long-presses.
private static final int QUICK_PRESS = 200;
- private static final int SCREEN_ORIENTATION_CHANGE_DETECTION_DELAY = 2;
+ private static final int START_INPUT_VIEW_DELAY_WHEN_SCREEN_ORIENTATION_STARTED = 10;
private static final int ACCUMULATE_START_INPUT_VIEW_DELAY = 20;
private static final int RESTORE_KEYBOARD_STATE_DELAY = 500;
@@ -198,8 +198,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// Member variables for remembering the current device orientation.
private int mDisplayOrientation;
- private int mDisplayWidth;
- private int mDisplayHeight;
// Object for reacting to adding/removing a dictionary pack.
private BroadcastReceiver mDictionaryPackInstallReceiver =
@@ -219,31 +217,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 5;
private static final int MSG_SPACE_TYPED = 6;
private static final int MSG_SET_BIGRAM_PREDICTIONS = 7;
- private static final int MSG_CONFIRM_ORIENTATION_CHANGE = 8;
+ private static final int MSG_START_ORIENTATION_CHANGE = 8;
private static final int MSG_START_INPUT_VIEW = 9;
private static final int MSG_RESTORE_KEYBOARD_LAYOUT = 10;
- private static class OrientationChangeArgs {
- public final int mOldWidth;
- public final int mOldHeight;
- private int mRetryCount;
-
- public OrientationChangeArgs(int oldw, int oldh) {
- mOldWidth = oldw;
- mOldHeight = oldh;
- mRetryCount = 0;
- }
-
- public boolean hasTimedOut() {
- mRetryCount++;
- return mRetryCount >= 10;
- }
-
- public boolean hasOrientationChangeFinished(DisplayMetrics dm) {
- return dm.widthPixels != mOldWidth && dm.heightPixels != mOldHeight;
- }
- }
-
public UIHandler(LatinIME outerInstance) {
super(outerInstance);
}
@@ -291,18 +268,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
(LatinKeyboard)msg.obj);
}
break;
- case MSG_CONFIRM_ORIENTATION_CHANGE: {
- final OrientationChangeArgs args = (OrientationChangeArgs)msg.obj;
- final Resources res = latinIme.mResources;
- final DisplayMetrics dm = res.getDisplayMetrics();
- if (args.hasTimedOut() || args.hasOrientationChangeFinished(dm)) {
- latinIme.setDisplayGeometry(res.getConfiguration(), dm);
- } else {
- // It seems orientation changing is on going.
- postConfirmOrientationChange(args);
- }
- break;
- }
case MSG_START_INPUT_VIEW:
latinIme.onStartInputView((EditorInfo)msg.obj, false);
break;
@@ -411,22 +376,16 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
- private void postConfirmOrientationChange(OrientationChangeArgs args) {
- removeMessages(MSG_CONFIRM_ORIENTATION_CHANGE);
- // Will confirm whether orientation change has finished or not again.
- sendMessageDelayed(obtainMessage(MSG_CONFIRM_ORIENTATION_CHANGE, args),
- SCREEN_ORIENTATION_CHANGE_DETECTION_DELAY);
- }
-
- public void startOrientationChanging(int oldw, int oldh) {
- postConfirmOrientationChange(new OrientationChangeArgs(oldw, oldh));
+ public void startOrientationChanging() {
+ sendMessageDelayed(obtainMessage(MSG_START_ORIENTATION_CHANGE),
+ START_INPUT_VIEW_DELAY_WHEN_SCREEN_ORIENTATION_STARTED);
final LatinIME latinIme = getOuterInstance();
latinIme.mKeyboardSwitcher.getKeyboardState().save();
postRestoreKeyboardLayout();
}
public boolean postStartInputView(EditorInfo attribute) {
- if (hasMessages(MSG_CONFIRM_ORIENTATION_CHANGE) || hasMessages(MSG_START_INPUT_VIEW)) {
+ if (hasMessages(MSG_START_ORIENTATION_CHANGE) || hasMessages(MSG_START_INPUT_VIEW)) {
removeMessages(MSG_START_INPUT_VIEW);
// Postpone onStartInputView by ACCUMULATE_START_INPUT_VIEW_DELAY and see if
// orientation change has finished.
@@ -438,12 +397,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
- private void setDisplayGeometry(Configuration conf, DisplayMetrics metric) {
- mDisplayOrientation = conf.orientation;
- mDisplayWidth = metric.widthPixels;
- mDisplayHeight = metric.heightPixels;
- }
-
@Override
public void onCreate() {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
@@ -481,7 +434,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
- setDisplayGeometry(res.getConfiguration(), res.getDisplayMetrics());
+ mDisplayOrientation = res.getConfiguration().orientation;
// Register to receive ringer mode change and network state change.
// Also receive installation and removal of a dictionary pack.
@@ -609,8 +562,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
public void onConfigurationChanged(Configuration conf) {
mSubtypeSwitcher.onConfigurationChanged(conf);
// If orientation changed while predicting, commit the change
- if (conf.orientation != mDisplayOrientation) {
- mHandler.startOrientationChanging(mDisplayWidth, mDisplayHeight);
+ if (mDisplayOrientation != conf.orientation) {
+ mDisplayOrientation = conf.orientation;
+ mHandler.startOrientationChanging();
final InputConnection ic = getCurrentInputConnection();
commitTyped(ic);
if (ic != null) ic.finishComposingText(); // For voice input