From c84bc3460d2fb386a1db2a2c8b135b746fa706cd Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 23 Aug 2011 17:57:02 +0900 Subject: Rename LatinKeyboardBaseView to LatinKeyboardView Bug: 5182291 Change-Id: I5089a14902b9f4ff1ab4f34e3f7a42aca5040d3e --- .../accessibility/AccessibleKeyboardViewProxy.java | 6 +- .../inputmethod/keyboard/KeyboardSwitcher.java | 8 +- .../inputmethod/keyboard/LatinKeyboard.java | 4 +- .../keyboard/LatinKeyboardBaseView.java | 691 --------------------- .../inputmethod/keyboard/LatinKeyboardView.java | 691 +++++++++++++++++++++ .../keyboard/PopupMiniKeyboardView.java | 4 +- .../android/inputmethod/keyboard/PopupPanel.java | 2 +- .../keyboard/SuddenJumpingTouchEventHandler.java | 2 +- .../com/android/inputmethod/latin/LatinIME.java | 12 +- 9 files changed, 710 insertions(+), 710 deletions(-) delete mode 100644 java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java create mode 100644 java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java (limited to 'java/src') 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/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index ec19cce6c..8bf82807a 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -61,7 +61,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private SharedPreferences mPrefs; private View mCurrentInputView; - private LatinKeyboardBaseView mKeyboardView; + private LatinKeyboardView mKeyboardView; private LatinIME mInputMethodService; private String mPackageName; private Resources mResources; @@ -745,7 +745,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } } - public LatinKeyboardBaseView getKeyboardView() { + public LatinKeyboardView getKeyboardView() { return mKeyboardView; } @@ -781,7 +781,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } } - mKeyboardView = (LatinKeyboardBaseView) mCurrentInputView.findViewById(R.id.keyboard_view); + mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); mKeyboardView.setKeyboardActionListener(mInputMethodService); // This always needs to be set since the accessibility state can @@ -819,7 +819,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha final LatinKeyboard keyboard = getLatinKeyboard(); if (keyboard != null && keyboard.needsAutoCorrectionSpacebarLed()) { final Key invalidatedKey = keyboard.onAutoCorrectionStateChanged(isAutoCorrection); - final LatinKeyboardBaseView keyboardView = getKeyboardView(); + final LatinKeyboardView keyboardView = getKeyboardView(); if (keyboardView != null) keyboardView.invalidateKey(invalidatedKey); } diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java index d31d3e39f..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) @@ -154,7 +154,7 @@ public class LatinKeyboard extends Keyboard { return newColor; } - public void updateShortcutKey(boolean available, LatinKeyboardBaseView view) { + public void updateShortcutKey(boolean available, LatinKeyboardView view) { if (mShortcutKey == null) return; mShortcutKey.setEnabled(available); 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 2af4594f4..000000000 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java +++ /dev/null @@ -1,691 +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.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, - SuddenJumpingTouchEventHandler.ProcessMotionEvent { - private static final String TAG = LatinKeyboardBaseView.class.getSimpleName(); - - 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 mPopupPanelCache = - new WeakHashMap(); - - /** 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 - 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); - - 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 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(); - } - - /** - * 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 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: - 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 new file mode 100644 index 000000000..be04b5a52 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -0,0 +1,691 @@ +/* + * 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.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 LatinKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler, + SuddenJumpingTouchEventHandler.ProcessMotionEvent { + private static final String TAG = LatinKeyboardView.class.getSimpleName(); + + 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 mPopupPanelCache = + new WeakHashMap(); + + /** 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 + 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) { + 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 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(); + } + + /** + * 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 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: + 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/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java index 1230dfb44..2396222bc 100644 --- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java @@ -41,7 +41,7 @@ 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; @@ -200,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 index 5344c27d4..c4251ccfc 100644 --- a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java +++ b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java @@ -78,7 +78,7 @@ public class SuddenJumpingTouchEventHandler { * 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}. + * {@link LatinKeyboardView}. */ private boolean handleSuddenJumping(MotionEvent me) { if (!mNeedsSuddenJumpingHack) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 48bcd3b92..394414d03 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -67,7 +67,7 @@ import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardSwitcher.KeyboardLayoutState; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.LatinKeyboard; -import com.android.inputmethod.keyboard.LatinKeyboardBaseView; +import com.android.inputmethod.keyboard.LatinKeyboardView; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -229,7 +229,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void handleMessage(Message msg) { final LatinIME latinIme = getOuterInstance(); final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher; - final LatinKeyboardBaseView inputView = switcher.getKeyboardView(); + final LatinKeyboardView inputView = switcher.getKeyboardView(); switch (msg.what) { case MSG_UPDATE_SUGGESTIONS: latinIme.updateSuggestions(); @@ -330,7 +330,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final LatinIME latinIme = getOuterInstance(); removeMessages(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR); removeMessages(MSG_DISMISS_LANGUAGE_ON_SPACEBAR); - final LatinKeyboardBaseView inputView = latinIme.mKeyboardSwitcher.getKeyboardView(); + final LatinKeyboardView inputView = latinIme.mKeyboardSwitcher.getKeyboardView(); if (inputView != null) { final LatinKeyboard keyboard = latinIme.mKeyboardSwitcher.getLatinKeyboard(); // The language is always displayed when the delay is negative. @@ -610,7 +610,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } final KeyboardSwitcher switcher = mKeyboardSwitcher; - LatinKeyboardBaseView inputView = switcher.getKeyboardView(); + LatinKeyboardView inputView = switcher.getKeyboardView(); if (DEBUG) { Log.d(TAG, "onStartInputView: attribute:" + ((attribute == null) ? "none" @@ -1498,7 +1498,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar commitTyped(getCurrentInputConnection()); mVoiceProxy.handleClose(); requestHideSelf(0); - LatinKeyboardBaseView inputView = mKeyboardSwitcher.getKeyboardView(); + LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) inputView.closing(); } @@ -2070,7 +2070,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (!mSettingsValues.mVibrateOn) { return; } - LatinKeyboardBaseView inputView = mKeyboardSwitcher.getKeyboardView(); + LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) { inputView.performHapticFeedback( HapticFeedbackConstants.KEYBOARD_TAP, -- cgit v1.2.3-83-g751a