aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java43
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java11
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java29
-rw-r--r--java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java227
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java594
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java670
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java14
-rw-r--r--java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java8
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java27
-rw-r--r--java/src/com/android/inputmethod/latin/CandidateView.java478
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java16
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java53
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsActivity.java6
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java2
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java19
17 files changed, 1410 insertions, 791 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
index 7199550a9..89adc15f2 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java
@@ -16,11 +16,15 @@
package com.android.inputmethod.accessibility;
+import android.content.Context;
import android.content.SharedPreferences;
import android.inputmethodservice.InputMethodService;
+import android.media.AudioManager;
import android.os.Looper;
import android.os.Message;
+import android.os.Vibrator;
import android.text.TextUtils;
+import android.view.KeyEvent;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
@@ -38,8 +42,14 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi
*/
private static final long DELAY_NO_HOVER_SELECTION = 250;
- private InputMethodService mInputMethod;
+ /**
+ * Duration of the key click vibration in milliseconds.
+ */
+ private static final long VIBRATE_KEY_CLICK = 50;
+ private InputMethodService mInputMethod;
+ private Vibrator mVibrator;
+ private AudioManager mAudioManager;
private AccessibilityHandler mAccessibilityHandler;
private static class AccessibilityHandler
@@ -84,6 +94,8 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi
private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) {
mInputMethod = inputMethod;
+ mVibrator = (Vibrator) inputMethod.getSystemService(Context.VIBRATOR_SERVICE);
+ mAudioManager = (AudioManager) inputMethod.getSystemService(Context.AUDIO_SERVICE);
mAccessibilityHandler = new AccessibilityHandler(this, inputMethod.getMainLooper());
}
@@ -107,6 +119,35 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi
}
/**
+ * Handle flick gestures by mapping them to directional pad keys.
+ */
+ @Override
+ public void onFlickGesture(int direction) {
+ final int keyEventCode;
+
+ switch (direction) {
+ case FlickGestureDetector.FLICK_LEFT:
+ sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT);
+ break;
+ case FlickGestureDetector.FLICK_RIGHT:
+ sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT);
+ break;
+ }
+ }
+
+ /**
+ * Provide haptic feedback and send the specified keyCode to the input
+ * connection as a pair of down/up events.
+ *
+ * @param keyCode
+ */
+ private void sendDownUpKeyEvents(int keyCode) {
+ mVibrator.vibrate(VIBRATE_KEY_CLICK);
+ mAudioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
+ mInputMethod.sendDownUpKeyEvents(keyCode);
+ }
+
+ /**
* When Accessibility is turned on, notifies the user that they are not
* currently hovering above a key. By default this will speak the currently
* entered text.
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java
index 12c59d0fc..c1e92bec8 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardActionListener.java
@@ -34,4 +34,15 @@ public interface AccessibleKeyboardActionListener {
* @param primaryCode the code of the key that was hovered over
*/
public void onHoverExit(int primaryCode);
+
+ /**
+ * @param direction the direction of the flick gesture, one of
+ * <ul>
+ * <li>{@link FlickGestureDetector#FLICK_UP}
+ * <li>{@link FlickGestureDetector#FLICK_DOWN}
+ * <li>{@link FlickGestureDetector#FLICK_LEFT}
+ * <li>{@link FlickGestureDetector#FLICK_RIGHT}
+ * </ul>
+ */
+ public void onFlickGesture(int direction);
}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index 96f7fc9f2..38d904a94 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -29,7 +29,7 @@ import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
import com.android.inputmethod.compat.MotionEventCompatUtils;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector;
-import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.LatinKeyboardBaseView;
import com.android.inputmethod.keyboard.PointerTracker;
public class AccessibleKeyboardViewProxy {
@@ -40,8 +40,9 @@ public class AccessibleKeyboardViewProxy {
private static final long DELAY_KEY_PRESS = 10;
private int mScaledEdgeSlop;
- private KeyboardView mView;
+ private LatinKeyboardBaseView mView;
private AccessibleKeyboardActionListener mListener;
+ private FlickGestureDetector mGestureDetector;
private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY;
private int mLastX = -1;
@@ -56,7 +57,7 @@ public class AccessibleKeyboardViewProxy {
return sInstance;
}
- public static void setView(KeyboardView view) {
+ public static void setView(LatinKeyboardBaseView view) {
sInstance.mView = view;
}
@@ -71,6 +72,7 @@ public class AccessibleKeyboardViewProxy {
paint.setAntiAlias(true);
paint.setColor(Color.YELLOW);
+ mGestureDetector = new KeyboardFlickGestureDetector(context);
mScaledEdgeSlop = ViewConfiguration.get(context).getScaledEdgeSlop();
}
@@ -110,7 +112,10 @@ public class AccessibleKeyboardViewProxy {
* @return {@code true} if the event is handled
*/
public boolean onHoverEvent(MotionEvent event, PointerTracker tracker) {
- return onTouchExplorationEvent(event, tracker);
+ if (mGestureDetector.onHoverEvent(event, this, tracker))
+ return true;
+
+ return onHoverEventInternal(event, tracker);
}
public boolean dispatchTouchEvent(MotionEvent event) {
@@ -128,7 +133,7 @@ public class AccessibleKeyboardViewProxy {
* @param event The touch exploration hover event.
* @return {@code true} if the event was handled
*/
- private boolean onTouchExplorationEvent(MotionEvent event, PointerTracker tracker) {
+ /*package*/ boolean onHoverEventInternal(MotionEvent event, PointerTracker tracker) {
final int x = (int) event.getX();
final int y = (int) event.getY();
@@ -198,4 +203,18 @@ public class AccessibleKeyboardViewProxy {
tracker.onDownEvent(x, y, eventTime, null);
tracker.onUpEvent(x, y, eventTime + DELAY_KEY_PRESS, null);
}
+
+ private class KeyboardFlickGestureDetector extends FlickGestureDetector {
+ public KeyboardFlickGestureDetector(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean onFlick(MotionEvent e1, MotionEvent e2, int direction) {
+ if (mListener != null) {
+ mListener.onFlickGesture(direction);
+ }
+ return true;
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java b/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java
new file mode 100644
index 000000000..9d99e3131
--- /dev/null
+++ b/java/src/com/android/inputmethod/accessibility/FlickGestureDetector.java
@@ -0,0 +1,227 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.os.Message;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.inputmethod.compat.MotionEventCompatUtils;
+import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
+
+/**
+ * Detects flick gestures within a stream of hover events.
+ * <p>
+ * A flick gesture is defined as a stream of hover events with the following
+ * properties:
+ * <ul>
+ * <li>Begins with a {@link MotionEventCompatUtils#ACTION_HOVER_ENTER} event
+ * <li>Contains any number of {@link MotionEventCompatUtils#ACTION_HOVER_MOVE}
+ * events
+ * <li>Ends with a {@link MotionEventCompatUtils#ACTION_HOVER_EXIT} event
+ * <li>Maximum duration of 250 milliseconds
+ * <li>Minimum distance between enter and exit points must be at least equal to
+ * scaled double tap slop (see
+ * {@link ViewConfiguration#getScaledDoubleTapSlop()})
+ * </ul>
+ * <p>
+ * Initial enter events are intercepted and cached until the stream fails to
+ * satisfy the constraints defined above, at which point the cached enter event
+ * is sent to its source {@link AccessibleKeyboardViewProxy} and subsequent move
+ * and exit events are ignored.
+ */
+public abstract class FlickGestureDetector {
+ public static final int FLICK_UP = 0;
+ public static final int FLICK_RIGHT = 1;
+ public static final int FLICK_LEFT = 2;
+ public static final int FLICK_DOWN = 3;
+
+ private final FlickHandler mFlickHandler;
+ private final int mFlickRadiusSquare;
+
+ private AccessibleKeyboardViewProxy mCachedView;
+ private PointerTracker mCachedTracker;
+ private MotionEvent mCachedHoverEnter;
+
+ private static class FlickHandler extends StaticInnerHandlerWrapper<FlickGestureDetector> {
+ private static final int MSG_FLICK_TIMEOUT = 1;
+
+ /** The maximum duration of a flick gesture in milliseconds. */
+ private static final int DELAY_FLICK_TIMEOUT = 250;
+
+ public FlickHandler(FlickGestureDetector outerInstance) {
+ super(outerInstance);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final FlickGestureDetector gestureDetector = getOuterInstance();
+
+ switch (msg.what) {
+ case MSG_FLICK_TIMEOUT:
+ gestureDetector.clearFlick(true);
+ }
+ }
+
+ public void startFlickTimeout() {
+ cancelFlickTimeout();
+ sendEmptyMessageDelayed(MSG_FLICK_TIMEOUT, DELAY_FLICK_TIMEOUT);
+ }
+
+ public void cancelFlickTimeout() {
+ removeMessages(MSG_FLICK_TIMEOUT);
+ }
+ }
+
+ /**
+ * Creates a new flick gesture detector.
+ *
+ * @param context The parent context.
+ */
+ public FlickGestureDetector(Context context) {
+ final int doubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+
+ mFlickHandler = new FlickHandler(this);
+ mFlickRadiusSquare = doubleTapSlop * doubleTapSlop;
+ }
+
+ /**
+ * Processes motion events to detect flick gestures.
+ *
+ * @param event The current event.
+ * @param view The source of the event.
+ * @param tracker A pointer tracker for the event.
+ * @return {@code true} if the event was handled.
+ */
+ public boolean onHoverEvent(MotionEvent event, AccessibleKeyboardViewProxy view,
+ PointerTracker tracker) {
+ // Always cache and consume the first hover event.
+ if (event.getAction() == MotionEventCompatUtils.ACTION_HOVER_ENTER) {
+ mCachedView = view;
+ mCachedTracker = tracker;
+ mCachedHoverEnter = MotionEvent.obtain(event);
+ mFlickHandler.startFlickTimeout();
+ return true;
+ }
+
+ // Stop if the event has already been canceled.
+ if (mCachedHoverEnter == null) {
+ return false;
+ }
+
+ final float distanceSquare = calculateDistanceSquare(mCachedHoverEnter, event);
+ final long timeout = event.getEventTime() - mCachedHoverEnter.getEventTime();
+
+ switch (event.getAction()) {
+ case MotionEventCompatUtils.ACTION_HOVER_MOVE:
+ // Consume all valid move events before timeout.
+ return true;
+ case MotionEventCompatUtils.ACTION_HOVER_EXIT:
+ // Ignore exit events outside the flick radius.
+ if (distanceSquare < mFlickRadiusSquare) {
+ clearFlick(true);
+ return false;
+ } else {
+ return dispatchFlick(mCachedHoverEnter, event);
+ }
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Clears the cached flick information and optionally forwards the event to
+ * the source view's internal hover event handler.
+ *
+ * @param sendCachedEvent Set to {@code true} to forward the hover event to
+ * the source view.
+ */
+ private void clearFlick(boolean sendCachedEvent) {
+ mFlickHandler.cancelFlickTimeout();
+
+ if (mCachedHoverEnter != null) {
+ if (sendCachedEvent) {
+ mCachedView.onHoverEventInternal(mCachedHoverEnter, mCachedTracker);
+ }
+ mCachedHoverEnter.recycle();
+ mCachedHoverEnter = null;
+ }
+
+ mCachedTracker = null;
+ mCachedView = null;
+ }
+
+ /**
+ * Computes the direction of a flick gesture and forwards it to
+ * {@link #onFlick(MotionEvent, MotionEvent, int)} for handling.
+ *
+ * @param e1 The {@link MotionEventCompatUtils#ACTION_HOVER_ENTER} event
+ * where the flick started.
+ * @param e2 The {@link MotionEventCompatUtils#ACTION_HOVER_EXIT} event
+ * where the flick ended.
+ * @return {@code true} if the flick event was handled.
+ */
+ private boolean dispatchFlick(MotionEvent e1, MotionEvent e2) {
+ clearFlick(false);
+
+ final float dX = e2.getX() - e1.getX();
+ final float dY = e2.getY() - e1.getY();
+ final int direction;
+
+ if (dY > dX) {
+ if (dY > -dX) {
+ direction = FLICK_DOWN;
+ } else {
+ direction = FLICK_LEFT;
+ }
+ } else {
+ if (dY > -dX) {
+ direction = FLICK_RIGHT;
+ } else {
+ direction = FLICK_UP;
+ }
+ }
+
+ return onFlick(e1, e2, direction);
+ }
+
+ private float calculateDistanceSquare(MotionEvent e1, MotionEvent e2) {
+ final float dX = e2.getX() - e1.getX();
+ final float dY = e2.getY() - e1.getY();
+ return (dX * dX) + (dY * dY);
+ }
+
+ /**
+ * Handles a detected flick gesture.
+ *
+ * @param e1 The {@link MotionEventCompatUtils#ACTION_HOVER_ENTER} event
+ * where the flick started.
+ * @param e2 The {@link MotionEventCompatUtils#ACTION_HOVER_EXIT} event
+ * where the flick ended.
+ * @param direction The direction of the flick event, one of:
+ * <ul>
+ * <li>{@link #FLICK_UP}
+ * <li>{@link #FLICK_DOWN}
+ * <li>{@link #FLICK_LEFT}
+ * <li>{@link #FLICK_RIGHT}
+ * </ul>
+ * @return {@code true} if the flick event was handled.
+ */
+ public abstract boolean onFlick(MotionEvent e1, MotionEvent e2, int direction);
+}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 203cde915..f3e023d3e 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -17,7 +17,6 @@
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.Bitmap;
@@ -32,39 +31,24 @@ import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Message;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.TypedValue;
-import android.view.GestureDetector;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.PopupWindow;
import android.widget.TextView;
-import com.android.inputmethod.accessibility.AccessibilityUtils;
-import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.compat.FrameLayoutCompatUtils;
-import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder;
-import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
-import com.android.inputmethod.keyboard.internal.SwipeTracker;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.WeakHashMap;
/**
- * A view that renders a virtual {@link Keyboard}. It handles rendering of keys and detecting key
- * presses and touch movements.
+ * A view that renders a virtual {@link Keyboard}.
*
* @attr ref R.styleable#KeyboardView_backgroundDimAmount
* @attr ref R.styleable#KeyboardView_keyBackground
- * @attr ref R.styleable#KeyboardView_keyHysteresisDistance
* @attr ref R.styleable#KeyboardView_keyLetterRatio
* @attr ref R.styleable#KeyboardView_keyLargeLetterRatio
* @attr ref R.styleable#KeyboardView_keyLabelRatio
@@ -85,29 +69,17 @@ import java.util.WeakHashMap;
* @attr ref R.styleable#KeyboardView_keyHintLabelColor
* @attr ref R.styleable#KeyboardView_keyUppercaseLetterInactivatedColor
* @attr ref R.styleable#KeyboardView_keyUppercaseLetterActivatedColor
- * @attr ref R.styleable#KeyboardView_verticalCorrection
- * @attr ref R.styleable#KeyboardView_popupLayout
* @attr ref R.styleable#KeyboardView_shadowColor
* @attr ref R.styleable#KeyboardView_shadowRadius
*/
public class KeyboardView extends View implements PointerTracker.UIProxy {
- private static final String TAG = KeyboardView.class.getSimpleName();
private static final boolean DEBUG_KEYBOARD_GRID = false;
- private static final boolean ENABLE_CAPSLOCK_BY_LONGPRESS = true;
- private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true;
-
- // Timing constants
- private final int mKeyRepeatInterval;
-
// Miscellaneous constants
private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
// XML attribute
private final float mBackgroundDimAmount;
- private final float mKeyHysteresisDistance;
- private final float mVerticalCorrection;
- private final int mPopupLayout;
// HORIZONTAL ELLIPSIS "...", character for popup hint.
private static final String POPUP_HINT_CHAR = "\u2026";
@@ -124,32 +96,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
private int mDelayAfterPreview;
private ViewGroup mPreviewPlacer;
- // Mini keyboard
- private PopupWindow mPopupWindow;
- private PopupPanel mPopupMiniKeyboardPanel;
- private final WeakHashMap<Key, PopupPanel> mPopupPanelCache =
- new WeakHashMap<Key, PopupPanel>();
-
- /** Listener for {@link KeyboardActionListener}. */
- private KeyboardActionListener mKeyboardActionListener;
-
- private final ArrayList<PointerTracker> mPointerTrackers = new ArrayList<PointerTracker>();
-
- // TODO: Let the PointerTracker class manage this pointer queue
- private final PointerTrackerQueue mPointerQueue = new PointerTrackerQueue();
-
- private final boolean mHasDistinctMultitouch;
- private int mOldPointerCount = 1;
- private int mOldKeyIndex;
-
- protected KeyDetector mKeyDetector = new KeyDetector();
-
- // Swipe gesture detector
- protected GestureDetector mGestureDetector;
- private final SwipeTracker mSwipeTracker = new SwipeTracker();
- private final int mSwipeThreshold;
- private final boolean mDisambiguateSwipe;
-
// Drawing
/** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/
private boolean mDrawPending;
@@ -182,12 +128,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
public static class UIHandler extends StaticInnerHandlerWrapper<KeyboardView> {
private static final int MSG_SHOW_KEY_PREVIEW = 1;
private static final int MSG_DISMISS_KEY_PREVIEW = 2;
- private static final int MSG_REPEAT_KEY = 3;
- private static final int MSG_LONGPRESS_KEY = 4;
- private static final int MSG_LONGPRESS_SHIFT_KEY = 5;
- private static final int MSG_IGNORE_DOUBLE_TAP = 6;
-
- private boolean mInKeyRepeat;
public UIHandler(KeyboardView outerInstance) {
super(outerInstance);
@@ -204,16 +144,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
case MSG_DISMISS_KEY_PREVIEW:
keyboardView.mPreviewText.setVisibility(View.INVISIBLE);
break;
- 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;
- case MSG_LONGPRESS_SHIFT_KEY:
- keyboardView.onLongPressShiftKey(tracker);
- break;
}
}
@@ -249,55 +179,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
removeMessages(MSG_DISMISS_KEY_PREVIEW);
}
- 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;
- }
-
- public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {
- cancelLongPressTimers();
- sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0, tracker), delay);
- }
-
- public void startLongPressShiftTimer(long delay, int keyIndex, PointerTracker tracker) {
- cancelLongPressTimers();
- if (ENABLE_CAPSLOCK_BY_LONGPRESS) {
- sendMessageDelayed(
- obtainMessage(MSG_LONGPRESS_SHIFT_KEY, keyIndex, 0, tracker), delay);
- }
- }
-
- public void cancelLongPressTimers() {
- removeMessages(MSG_LONGPRESS_KEY);
- removeMessages(MSG_LONGPRESS_SHIFT_KEY);
- }
-
- public void cancelKeyTimers() {
- cancelKeyRepeatTimer();
- cancelLongPressTimers();
- 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();
cancelAllShowKeyPreviews();
cancelAllDismissKeyPreviews();
}
@@ -394,6 +276,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
public int mKeyLetterSize;
public final int[] mCoordinates = new int[2];
+ private static final int PREVIEW_ALPHA = 240;
+
public KeyPreviewDrawParams(TypedArray a, KeyDrawParams keyDrawParams) {
mPreviewBackground = a.getDrawable(R.styleable.KeyboardView_keyPreviewBackground);
mPreviewLeftBackground = a.getDrawable(
@@ -402,6 +286,9 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
R.styleable.KeyboardView_keyPreviewRightBackground);
mPreviewSpacebarBackground = a.getDrawable(
R.styleable.KeyboardView_keyPreviewSpacebarBackground);
+ setAlpha(mPreviewBackground, PREVIEW_ALPHA);
+ setAlpha(mPreviewLeftBackground, PREVIEW_ALPHA);
+ setAlpha(mPreviewRightBackground, PREVIEW_ALPHA);
mPreviewOffset = a.getDimensionPixelOffset(
R.styleable.KeyboardView_keyPreviewOffset, 0);
mPreviewHeight = a.getDimensionPixelSize(
@@ -417,6 +304,12 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio);
mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio);
}
+
+ private static void setAlpha(Drawable drawable, int alpha) {
+ if (drawable == null)
+ return;
+ drawable.setAlpha(alpha);
+ }
}
public KeyboardView(Context context, AttributeSet attrs) {
@@ -431,10 +324,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
mKeyDrawParams = new KeyDrawParams(a);
mKeyPreviewDrawParams = new KeyPreviewDrawParams(a, mKeyDrawParams);
- mKeyHysteresisDistance = a.getDimensionPixelOffset(
- R.styleable.KeyboardView_keyHysteresisDistance, 0);
- mVerticalCorrection = a.getDimensionPixelOffset(
- R.styleable.KeyboardView_verticalCorrection, 0);
final int previewLayout = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0);
if (previewLayout != 0) {
mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null);
@@ -442,7 +331,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
mPreviewText = null;
mShowKeyPreviewPopup = false;
}
- mPopupLayout = a.getResourceId(R.styleable.KeyboardView_popupLayout, 0);
// TODO: Use Theme (android.R.styleable.Theme_backgroundDimAmount)
mBackgroundDimAmount = a.getFloat(R.styleable.KeyboardView_backgroundDimAmount, 0.5f);
a.recycle();
@@ -455,82 +343,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
mPaint.setAntiAlias(true);
mPaint.setTextAlign(Align.CENTER);
mPaint.setAlpha(255);
-
- mSwipeThreshold = (int) (500 * res.getDisplayMetrics().density);
- // TODO: Refer to frameworks/base/core/res/res/values/config.xml
- mDisambiguateSwipe = res.getBoolean(R.bool.config_swipeDisambiguation);
-
- GestureDetector.SimpleOnGestureListener listener =
- new GestureDetector.SimpleOnGestureListener() {
- private boolean mProcessingShiftDoubleTapEvent = false;
-
- @Override
- public boolean onFling(MotionEvent me1, MotionEvent me2, float velocityX,
- float velocityY) {
- final float absX = Math.abs(velocityX);
- final float absY = Math.abs(velocityY);
- float deltaY = me2.getY() - me1.getY();
- int travelY = getHeight() / 2; // Half the keyboard height
- mSwipeTracker.computeCurrentVelocity(1000);
- final float endingVelocityY = mSwipeTracker.getYVelocity();
- if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) {
- if (mDisambiguateSwipe && endingVelocityY >= velocityY / 4) {
- onSwipeDown();
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean onDoubleTap(MotionEvent firstDown) {
- if (ENABLE_CAPSLOCK_BY_DOUBLETAP && mKeyboard instanceof LatinKeyboard
- && ((LatinKeyboard) mKeyboard).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 = mHandler.isIgnoringDoubleTap();
- if (!ignoringDoubleTap)
- onDoubleTapShiftKey(tracker);
- return true;
- }
- // Otherwise these events should not be handled as double tap.
- mProcessingShiftDoubleTapEvent = false;
- }
- return mProcessingShiftDoubleTapEvent;
- }
- };
-
- final boolean ignoreMultitouch = true;
- mGestureDetector = new GestureDetector(getContext(), listener, null, ignoreMultitouch);
- mGestureDetector.setIsLongpressEnabled(false);
-
- mHasDistinctMultitouch = context.getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
- mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
}
// Read fraction value in TypedArray as float.
@@ -538,26 +350,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
return a.getFraction(index, 1000, 1000, 1) / 1000.0f;
}
- public void startIgnoringDoubleTap() {
- if (ENABLE_CAPSLOCK_BY_DOUBLETAP)
- mHandler.startIgnoringDoubleTap();
- }
-
- public void setOnKeyboardActionListener(KeyboardActionListener listener) {
- mKeyboardActionListener = listener;
- for (PointerTracker tracker : mPointerTrackers) {
- tracker.setOnKeyboardActionListener(listener);
- }
- }
-
- /**
- * Returns the {@link KeyboardActionListener} object.
- * @return the listener attached to this keyboard
- */
- protected KeyboardActionListener getOnKeyboardActionListener() {
- return mKeyboardActionListener;
- }
-
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO: Should notify InputMethodService instead?
@@ -572,24 +364,13 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
* @param keyboard the keyboard to display in this view
*/
public void setKeyboard(Keyboard keyboard) {
- if (mKeyboard != null) {
- dismissAllKeyPreviews();
- }
// Remove any pending messages, except dismissing preview
- mHandler.cancelKeyTimers();
mHandler.cancelAllShowKeyPreviews();
mKeyboard = keyboard;
LatinImeLogger.onSetKeyboard(keyboard);
- mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
- -getPaddingTop() + mVerticalCorrection);
- for (PointerTracker tracker : mPointerTrackers) {
- tracker.setKeyboard(keyboard, mKeyHysteresisDistance);
- }
requestLayout();
mKeyboardChanged = true;
invalidateAllKeys();
- mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
- mPopupPanelCache.clear();
final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap();
mKeyDrawParams.updateKeyHeight(keyHeight);
mKeyPreviewDrawParams.updateKeyHeight(keyHeight);
@@ -605,15 +386,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
}
/**
- * Returns whether the device has distinct multi-touch panel.
- * @return true if the device has distinct multi-touch panel.
- */
- @Override
- public boolean hasDistinctMultitouch() {
- return mHasDistinctMultitouch;
- }
-
- /**
* Enables or disables the key feedback popup. This is a popup that shows a magnified
* version of the depressed key. By default the preview is enabled.
* @param previewEnabled whether or not to enable the key feedback preview
@@ -634,23 +406,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
return mShowKeyPreviewPopup;
}
- /**
- * 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 onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Round up a little
@@ -741,7 +496,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
}
// Overlay a dark rectangle to dim the keyboard
- if (mPopupMiniKeyboardPanel != null) {
+ if (needsToDimKeyboard()) {
mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
canvas.drawRect(0, 0, width, height, mPaint);
}
@@ -751,6 +506,10 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
mDirtyRect.setEmpty();
}
+ protected boolean needsToDimKeyboard() {
+ return false;
+ }
+
private static void onBufferDrawKey(final Key key, final Canvas canvas, Paint paint,
KeyDrawParams params, boolean isManualTemporaryUpperCase) {
final boolean debugShowAlign = LatinImeLogger.sVISUALDEBUG;
@@ -1006,15 +765,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
canvas.translate(-x, -y);
}
- // TODO: clean up this method.
- private void dismissAllKeyPreviews() {
- for (PointerTracker tracker : mPointerTrackers) {
- tracker.setReleasedKeyGraphics();
- dismissKeyPreview(tracker);
- }
- }
-
- public void cancelAllMessage() {
+ public void cancelAllMessages() {
mHandler.cancelAllMessages();
}
@@ -1029,6 +780,11 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
}
@Override
+ public void cancelShowKeyPreview(PointerTracker tracker) {
+ mHandler.cancelShowKeyPreview(tracker);
+ }
+
+ @Override
public void dismissKeyPreview(PointerTracker tracker) {
if (mShowKeyPreviewPopup) {
mHandler.cancelShowKeyPreview(tracker);
@@ -1150,266 +906,11 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
invalidate(mInvalidatedKeyRect);
}
- private boolean openMiniKeyboardIfRequired(int keyIndex, PointerTracker tracker) {
- // Check if we have a popup layout specified first.
- if (mPopupLayout == 0) {
- return false;
- }
-
- final Key parentKey = tracker.getKey(keyIndex);
- if (parentKey == null)
- return false;
- boolean result = onLongPress(parentKey, tracker);
- if (result) {
- dismissAllKeyPreviews();
- tracker.onLongPressed(mPointerQueue);
- }
- return result;
- }
-
- private void onLongPressShiftKey(PointerTracker tracker) {
- tracker.onLongPressed(mPointerQueue);
- mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
- }
-
- 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 mPointerQueueueue.
- mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
- }
-
- // This default implementation returns a popup mini keyboard panel.
- // A derived class may return a language switcher popup panel, for instance.
- 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);
- miniKeyboardView.setOnKeyboardActionListener(new KeyboardActionListener() {
- @Override
- public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
- mKeyboardActionListener.onCodeInput(primaryCode, keyCodes, x, y);
- dismissMiniKeyboard();
- }
-
- @Override
- public void onTextInput(CharSequence text) {
- mKeyboardActionListener.onTextInput(text);
- dismissMiniKeyboard();
- }
-
- @Override
- public void onCancelInput() {
- mKeyboardActionListener.onCancelInput();
- dismissMiniKeyboard();
- }
-
- @Override
- public void onSwipeDown() {
- // Nothing to do.
- }
- @Override
- public void onPress(int primaryCode, boolean withSliding) {
- mKeyboardActionListener.onPress(primaryCode, withSliding);
- }
- @Override
- public void onRelease(int primaryCode, boolean withSliding) {
- mKeyboardActionListener.onRelease(primaryCode, withSliding);
- }
- });
-
- final Keyboard keyboard = new MiniKeyboardBuilder(this, mKeyboard.getPopupKeyboardResId(),
- parentKey, mKeyboard).build();
- miniKeyboardView.setKeyboard(keyboard);
-
- container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
- MEASURESPEC_UNSPECIFIED);
-
- return miniKeyboardView;
- }
-
- /**
- * 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) {
- 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);
- }
- mPopupMiniKeyboardPanel = popupPanel;
- popupPanel.showPanel(this, parentKey, tracker, mPopupWindow);
-
- invalidateAllKeys();
- return true;
- }
-
- private PointerTracker getPointerTracker(final int id) {
- final ArrayList<PointerTracker> pointers = mPointerTrackers;
- final KeyboardActionListener listener = mKeyboardActionListener;
-
- // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
- for (int i = pointers.size(); i <= id; i++) {
- final PointerTracker tracker =
- new PointerTracker(i, this, mHandler, mKeyDetector, this);
- if (mKeyboard != null)
- tracker.setKeyboard(mKeyboard, mKeyHysteresisDistance);
- if (listener != null)
- tracker.setOnKeyboardActionListener(listener);
- pointers.add(tracker);
- }
-
- return pointers.get(id);
- }
-
- public boolean isInSlidingKeyInput() {
- if (mPopupMiniKeyboardPanel != null) {
- return mPopupMiniKeyboardPanel.isInSlidingKeyInput();
- } else {
- return mPointerQueue.isInSlidingKeyInput();
- }
- }
-
- public int getPointerCount() {
- return mOldPointerCount;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent me) {
- 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 (!mHasDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
- return true;
- }
-
- // Track the last few movements to look for spurious swipes.
- mSwipeTracker.addMovement(me);
-
- // Gesture detector must be enabled only when mini-keyboard is not on the screen.
- if (mPopupMiniKeyboardPanel == null && mGestureDetector != null
- && mGestureDetector.onTouchEvent(me)) {
- dismissAllKeyPreviews();
- mHandler.cancelKeyTimers();
- return true;
- }
-
- final long eventTime = me.getEventTime();
- final int index = me.getActionIndex();
- final int id = me.getPointerId(index);
- final int x = (int)me.getX(index);
- final int y = (int)me.getY(index);
-
- // Needs to be called after the gesture detector gets a turn, as it may have displayed the
- // mini keyboard
- if (mPopupMiniKeyboardPanel != null) {
- return mPopupMiniKeyboardPanel.onTouchEvent(me);
- }
-
- if (mHandler.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()) {
- mHandler.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 (!mHasDistinctMultitouch) {
- // 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, null);
- if (action == MotionEvent.ACTION_UP)
- tracker.onUpEvent(x, y, eventTime, null);
- }
- } 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, null);
- } else if (pointerCount == 1 && oldPointerCount == 1) {
- tracker.onTouchEvent(action, x, y, eventTime, null);
- } else {
- Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
- + " (old " + oldPointerCount + ")");
- }
- return true;
- }
-
- final PointerTrackerQueue queue = mPointerQueue;
- if (action == MotionEvent.ACTION_MOVE) {
- for (int i = 0; i < pointerCount; i++) {
- final PointerTracker tracker = getPointerTracker(me.getPointerId(i));
- tracker.onMoveEvent((int)me.getX(i), (int)me.getY(i), eventTime, queue);
- }
- } else {
- final PointerTracker tracker = getPointerTracker(id);
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN:
- tracker.onDownEvent(x, y, eventTime, queue);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP:
- tracker.onUpEvent(x, y, eventTime, queue);
- break;
- case MotionEvent.ACTION_CANCEL:
- tracker.onCancelEvent(x, y, eventTime, queue);
- break;
- }
- }
-
- return true;
- }
-
- protected void onSwipeDown() {
- mKeyboardActionListener.onSwipeDown();
- }
-
public void closing() {
mPreviewText.setVisibility(View.GONE);
- mHandler.cancelAllMessages();
+ cancelAllMessages();
- dismissMiniKeyboard();
mDirtyRect.union(0, 0, getWidth(), getHeight());
- mPopupPanelCache.clear();
requestLayout();
}
@@ -1423,51 +924,4 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
super.onDetachedFromWindow();
closing();
}
-
- private boolean dismissMiniKeyboard() {
- if (mPopupWindow != null && mPopupWindow.isShowing()) {
- mPopupWindow.dismiss();
- mPopupMiniKeyboardPanel = null;
- invalidateAllKeys();
- return true;
- }
- return false;
- }
-
- public boolean handleBack() {
- return dismissMiniKeyboard();
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event)
- || super.dispatchTouchEvent(event);
- }
-
- 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);
- }
-
- public boolean onHoverEvent(MotionEvent event) {
- // Since reflection doesn't support calling superclass methods, this
- // method checks for the existence of onHoverEvent() in the View class
- // before returning a value.
- if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- final PointerTracker tracker = getPointerTracker(0);
- return AccessibleKeyboardViewProxy.getInstance().onHoverEvent(event, tracker);
- }
-
- return false;
- }
}
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
new file mode 100644
index 000000000..0a7ba05c8
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
@@ -0,0 +1,670 @@
+/*
+ * 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.os.Message;
+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.keyboard.internal.MiniKeyboardBuilder;
+import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
+import com.android.inputmethod.keyboard.internal.SwipeTracker;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
+
+import java.util.ArrayList;
+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 {
+ private static final String TAG = LatinKeyboardBaseView.class.getSimpleName();
+
+ private static final boolean ENABLE_CAPSLOCK_BY_LONGPRESS = true;
+ private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true;
+
+ // Timing constants
+ private final int mKeyRepeatInterval;
+
+ // XML attribute
+ private final float mKeyHysteresisDistance;
+ private final float mVerticalCorrection;
+ private final int mPopupLayout;
+
+ // Mini keyboard
+ private PopupWindow mPopupWindow;
+ private PopupPanel mPopupMiniKeyboardPanel;
+ private final WeakHashMap<Key, PopupPanel> mPopupPanelCache =
+ new WeakHashMap<Key, PopupPanel>();
+
+ /** Listener for {@link KeyboardActionListener}. */
+ private KeyboardActionListener mKeyboardActionListener;
+
+ private final ArrayList<PointerTracker> mPointerTrackers = new ArrayList<PointerTracker>();
+
+ // TODO: Let the PointerTracker class manage this pointer queue
+ private final PointerTrackerQueue mPointerQueue = new PointerTrackerQueue();
+
+ private final boolean mHasDistinctMultitouch;
+ private int mOldPointerCount = 1;
+ private int mOldKeyIndex;
+
+ protected KeyDetector mKeyDetector = new KeyDetector();
+
+ // Swipe gesture detector
+ protected GestureDetector mGestureDetector;
+ private final SwipeTracker mSwipeTracker = new SwipeTracker();
+ private final int mSwipeThreshold;
+ private final boolean mDisambiguateSwipe;
+
+ private final UIHandler mHandler = new UIHandler(this);
+
+ public static class UIHandler extends StaticInnerHandlerWrapper<LatinKeyboardBaseView> {
+ private static final int MSG_REPEAT_KEY = 3;
+ private static final int MSG_LONGPRESS_KEY = 4;
+ private static final int MSG_LONGPRESS_SHIFT_KEY = 5;
+ private static final int MSG_IGNORE_DOUBLE_TAP = 6;
+
+ private boolean mInKeyRepeat;
+
+ public UIHandler(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;
+ case MSG_LONGPRESS_SHIFT_KEY:
+ keyboardView.onLongPressShiftKey(tracker);
+ break;
+ }
+ }
+
+ 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;
+ }
+
+ public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {
+ cancelLongPressTimers();
+ sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0, tracker), delay);
+ }
+
+ public void startLongPressShiftTimer(long delay, int keyIndex, PointerTracker tracker) {
+ cancelLongPressTimers();
+ if (ENABLE_CAPSLOCK_BY_LONGPRESS) {
+ sendMessageDelayed(
+ obtainMessage(MSG_LONGPRESS_SHIFT_KEY, keyIndex, 0, tracker), delay);
+ }
+ }
+
+ public void cancelLongPressTimers() {
+ removeMessages(MSG_LONGPRESS_KEY);
+ removeMessages(MSG_LONGPRESS_SHIFT_KEY);
+ }
+
+ public void cancelKeyTimers() {
+ cancelKeyRepeatTimer();
+ cancelLongPressTimers();
+ 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();
+ }
+ }
+
+ 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);
+ mKeyHysteresisDistance = a.getDimensionPixelOffset(
+ R.styleable.KeyboardView_keyHysteresisDistance, 0);
+ mVerticalCorrection = a.getDimensionPixelOffset(
+ R.styleable.KeyboardView_verticalCorrection, 0);
+
+ mPopupLayout = a.getResourceId(R.styleable.KeyboardView_popupLayout, 0);
+ // TODO: Use Theme (android.R.styleable.Theme_backgroundDimAmount)
+ a.recycle();
+
+ final Resources res = getResources();
+
+ mSwipeThreshold = (int) (500 * res.getDisplayMetrics().density);
+ // TODO: Refer to frameworks/base/core/res/res/values/config.xml
+ mDisambiguateSwipe = res.getBoolean(R.bool.config_swipeDisambiguation);
+
+ GestureDetector.SimpleOnGestureListener listener =
+ new GestureDetector.SimpleOnGestureListener() {
+ private boolean mProcessingShiftDoubleTapEvent = false;
+
+ @Override
+ public boolean onFling(MotionEvent me1, MotionEvent me2, float velocityX,
+ float velocityY) {
+ final float absX = Math.abs(velocityX);
+ final float absY = Math.abs(velocityY);
+ float deltaY = me2.getY() - me1.getY();
+ int travelY = getHeight() / 2; // Half the keyboard height
+ mSwipeTracker.computeCurrentVelocity(1000);
+ final float endingVelocityY = mSwipeTracker.getYVelocity();
+ if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) {
+ if (mDisambiguateSwipe && endingVelocityY >= velocityY / 4) {
+ onSwipeDown();
+ return true;
+ }
+ }
+ return 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 = mHandler.isIgnoringDoubleTap();
+ if (!ignoringDoubleTap)
+ onDoubleTapShiftKey(tracker);
+ return true;
+ }
+ // Otherwise these events should not be handled as double tap.
+ mProcessingShiftDoubleTapEvent = false;
+ }
+ return mProcessingShiftDoubleTapEvent;
+ }
+ };
+
+ final boolean ignoreMultitouch = true;
+ mGestureDetector = new GestureDetector(getContext(), listener, null, ignoreMultitouch);
+ mGestureDetector.setIsLongpressEnabled(false);
+
+ mHasDistinctMultitouch = context.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
+ mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
+ }
+
+ public void startIgnoringDoubleTap() {
+ if (ENABLE_CAPSLOCK_BY_DOUBLETAP)
+ mHandler.startIgnoringDoubleTap();
+ }
+
+ public void setOnKeyboardActionListener(KeyboardActionListener listener) {
+ mKeyboardActionListener = listener;
+ for (PointerTracker tracker : mPointerTrackers) {
+ tracker.setOnKeyboardActionListener(listener);
+ }
+ }
+
+ /**
+ * Returns the {@link KeyboardActionListener} object.
+ * @return the listener attached to this keyboard
+ */
+ protected KeyboardActionListener getOnKeyboardActionListener() {
+ return mKeyboardActionListener;
+ }
+
+ /**
+ * 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) {
+ if (getKeyboard() != null) {
+ dismissAllKeyPreviews();
+ }
+ // Remove any pending messages, except dismissing preview
+ mHandler.cancelKeyTimers();
+ super.setKeyboard(keyboard);
+ mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
+ -getPaddingTop() + mVerticalCorrection);
+ for (PointerTracker tracker : mPointerTrackers) {
+ tracker.setKeyboard(keyboard, mKeyHysteresisDistance);
+ }
+ mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth());
+ 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();
+ }
+
+ // TODO: clean up this method.
+ private void dismissAllKeyPreviews() {
+ for (PointerTracker tracker : mPointerTrackers) {
+ tracker.setReleasedKeyGraphics();
+ dismissKeyPreview(tracker);
+ }
+ }
+
+ @Override
+ public void cancelAllMessages() {
+ mHandler.cancelAllMessages();
+ super.cancelAllMessages();
+ }
+
+ private boolean openMiniKeyboardIfRequired(int keyIndex, PointerTracker tracker) {
+ // Check if we have a popup layout specified first.
+ if (mPopupLayout == 0) {
+ return false;
+ }
+
+ final Key parentKey = tracker.getKey(keyIndex);
+ if (parentKey == null)
+ return false;
+ boolean result = onLongPress(parentKey, tracker);
+ if (result) {
+ dismissAllKeyPreviews();
+ tracker.onLongPressed(mPointerQueue);
+ }
+ return result;
+ }
+
+ private void onLongPressShiftKey(PointerTracker tracker) {
+ tracker.onLongPressed(mPointerQueue);
+ mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
+ }
+
+ 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 mPointerQueueueue.
+ mKeyboardActionListener.onCodeInput(Keyboard.CODE_CAPSLOCK, null, 0, 0);
+ }
+
+ // This default implementation returns a popup mini keyboard panel.
+ // A derived class may return a language switcher popup panel, for instance.
+ 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);
+ miniKeyboardView.setOnKeyboardActionListener(new KeyboardActionListener() {
+ @Override
+ public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
+ mKeyboardActionListener.onCodeInput(primaryCode, keyCodes, x, y);
+ dismissMiniKeyboard();
+ }
+
+ @Override
+ public void onTextInput(CharSequence text) {
+ mKeyboardActionListener.onTextInput(text);
+ dismissMiniKeyboard();
+ }
+
+ @Override
+ public void onCancelInput() {
+ mKeyboardActionListener.onCancelInput();
+ dismissMiniKeyboard();
+ }
+
+ @Override
+ public void onSwipeDown() {
+ // Nothing to do.
+ }
+ @Override
+ public void onPress(int primaryCode, boolean withSliding) {
+ mKeyboardActionListener.onPress(primaryCode, withSliding);
+ }
+ @Override
+ public void onRelease(int primaryCode, boolean withSliding) {
+ mKeyboardActionListener.onRelease(primaryCode, withSliding);
+ }
+ });
+
+ final Keyboard parentKeyboard = getKeyboard();
+ final Keyboard miniKeyboard = new MiniKeyboardBuilder(
+ this, parentKeyboard.getPopupKeyboardResId(), 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 mPopupMiniKeyboardPanel != null;
+ }
+
+ /**
+ * 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) {
+ 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);
+ }
+ mPopupMiniKeyboardPanel = popupPanel;
+ popupPanel.showPanel(this, parentKey, tracker, mPopupWindow);
+
+ invalidateAllKeys();
+ return true;
+ }
+
+ private PointerTracker getPointerTracker(final int id) {
+ final ArrayList<PointerTracker> pointers = mPointerTrackers;
+ final KeyboardActionListener listener = mKeyboardActionListener;
+ final Keyboard keyboard = getKeyboard();
+
+ // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
+ for (int i = pointers.size(); i <= id; i++) {
+ final PointerTracker tracker =
+ new PointerTracker(i, this, mHandler, mKeyDetector, this);
+ if (keyboard != null)
+ tracker.setKeyboard(keyboard, mKeyHysteresisDistance);
+ if (listener != null)
+ tracker.setOnKeyboardActionListener(listener);
+ pointers.add(tracker);
+ }
+
+ return pointers.get(id);
+ }
+
+ public boolean isInSlidingKeyInput() {
+ if (mPopupMiniKeyboardPanel != null) {
+ return mPopupMiniKeyboardPanel.isInSlidingKeyInput();
+ } else {
+ return mPointerQueue.isInSlidingKeyInput();
+ }
+ }
+
+ public int getPointerCount() {
+ return mOldPointerCount;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent me) {
+ 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 (!mHasDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
+ return true;
+ }
+
+ // Track the last few movements to look for spurious swipes.
+ mSwipeTracker.addMovement(me);
+
+ // Gesture detector must be enabled only when mini-keyboard is not on the screen.
+ if (mPopupMiniKeyboardPanel == null && mGestureDetector != null
+ && mGestureDetector.onTouchEvent(me)) {
+ dismissAllKeyPreviews();
+ mHandler.cancelKeyTimers();
+ return true;
+ }
+
+ final long eventTime = me.getEventTime();
+ final int index = me.getActionIndex();
+ final int id = me.getPointerId(index);
+ final int x = (int)me.getX(index);
+ final int y = (int)me.getY(index);
+
+ // Needs to be called after the gesture detector gets a turn, as it may have displayed the
+ // mini keyboard
+ if (mPopupMiniKeyboardPanel != null) {
+ return mPopupMiniKeyboardPanel.onTouchEvent(me);
+ }
+
+ if (mHandler.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()) {
+ mHandler.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 (!mHasDistinctMultitouch) {
+ // 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, null);
+ if (action == MotionEvent.ACTION_UP)
+ tracker.onUpEvent(x, y, eventTime, null);
+ }
+ } 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, null);
+ } else if (pointerCount == 1 && oldPointerCount == 1) {
+ tracker.onTouchEvent(action, x, y, eventTime, null);
+ } else {
+ Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
+ + " (old " + oldPointerCount + ")");
+ }
+ return true;
+ }
+
+ final PointerTrackerQueue queue = mPointerQueue;
+ if (action == MotionEvent.ACTION_MOVE) {
+ for (int i = 0; i < pointerCount; i++) {
+ final PointerTracker tracker = getPointerTracker(me.getPointerId(i));
+ tracker.onMoveEvent((int)me.getX(i), (int)me.getY(i), eventTime, queue);
+ }
+ } else {
+ final PointerTracker tracker = getPointerTracker(id);
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ tracker.onDownEvent(x, y, eventTime, queue);
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ tracker.onUpEvent(x, y, eventTime, queue);
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ tracker.onCancelEvent(x, y, eventTime, queue);
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ protected void onSwipeDown() {
+ mKeyboardActionListener.onSwipeDown();
+ }
+
+ @Override
+ public void closing() {
+ super.closing();
+ dismissMiniKeyboard();
+ mPopupPanelCache.clear();
+ }
+
+ private boolean dismissMiniKeyboard() {
+ if (mPopupWindow != null && mPopupWindow.isShowing()) {
+ mPopupWindow.dismiss();
+ mPopupMiniKeyboardPanel = null;
+ invalidateAllKeys();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean handleBack() {
+ return dismissMiniKeyboard();
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+ return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event)
+ || super.dispatchTouchEvent(event);
+ }
+
+ 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);
+ }
+
+ public boolean onHoverEvent(MotionEvent event) {
+ // Since reflection doesn't support calling superclass methods, this
+ // method checks for the existence of onHoverEvent() in the View class
+ // before returning a value.
+ if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+ final PointerTracker tracker = getPointerTracker(0);
+ return AccessibleKeyboardViewProxy.getInstance().onHoverEvent(event, tracker);
+ }
+
+ return false;
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 75d022b52..510bc16b4 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -27,7 +27,7 @@ import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.Utils;
// TODO: We should remove this class
-public class LatinKeyboardView extends KeyboardView {
+public class LatinKeyboardView extends LatinKeyboardBaseView {
private static final String TAG = LatinKeyboardView.class.getSimpleName();
private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 6228cc8b2..68284cd1b 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -21,7 +21,7 @@ import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
-import com.android.inputmethod.keyboard.KeyboardView.UIHandler;
+import com.android.inputmethod.keyboard.LatinKeyboardBaseView.UIHandler;
import com.android.inputmethod.keyboard.internal.PointerTrackerKeyState;
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
import com.android.inputmethod.latin.LatinImeLogger;
@@ -42,8 +42,8 @@ public class PointerTracker {
public interface UIProxy {
public void invalidateKey(Key key);
public void showKeyPreview(int keyIndex, PointerTracker tracker);
+ public void cancelShowKeyPreview(PointerTracker tracker);
public void dismissKeyPreview(PointerTracker tracker);
- public boolean hasDistinctMultitouch();
}
public final int mPointerId;
@@ -53,7 +53,7 @@ public class PointerTracker {
private final int mLongPressKeyTimeout;
private final int mLongPressShiftKeyTimeout;
- private final KeyboardView mKeyboardView;
+ private final LatinKeyboardBaseView mKeyboardView;
private final UIProxy mProxy;
private final UIHandler mHandler;
private final KeyDetector mKeyDetector;
@@ -112,7 +112,7 @@ public class PointerTracker {
public void onSwipeDown() {}
};
- public PointerTracker(int id, KeyboardView keyboardView, UIHandler handler,
+ public PointerTracker(int id, LatinKeyboardBaseView keyboardView, UIHandler handler,
KeyDetector keyDetector, UIProxy proxy) {
if (proxy == null || handler == null || keyDetector == null)
throw new NullPointerException();
@@ -123,7 +123,7 @@ public class PointerTracker {
mKeyDetector = keyDetector;
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
mKeyState = new PointerTrackerKeyState(keyDetector);
- mHasDistinctMultitouch = proxy.hasDistinctMultitouch();
+ mHasDistinctMultitouch = keyboardView.hasDistinctMultitouch();
final Resources res = mKeyboardView.getResources();
mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
@@ -504,7 +504,7 @@ public class PointerTracker {
private void onUpEventInternal(int x, int y, long eventTime,
boolean updateReleasedKeyGraphics) {
mHandler.cancelKeyTimers();
- mHandler.cancelShowKeyPreview(this);
+ mProxy.cancelShowKeyPreview(this);
mIsInSlidingKeyInput = false;
final PointerTrackerKeyState keyState = mKeyState;
final int keyX, keyY;
@@ -564,7 +564,7 @@ public class PointerTracker {
private void onCancelEventInternal() {
mHandler.cancelKeyTimers();
- mHandler.cancelShowKeyPreview(this);
+ mProxy.cancelShowKeyPreview(this);
dismissKeyPreview();
setReleasedKeyGraphics(mKeyState.getKeyIndex());
mIsInSlidingKeyInput = false;
diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
index 3b8c36487..959427aad 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
@@ -31,7 +31,7 @@ import com.android.inputmethod.latin.R;
* A view that renders a virtual {@link MiniKeyboard}. It handles rendering of keys and detecting
* key presses and touch movements.
*/
-public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel {
+public class PopupMiniKeyboardView extends LatinKeyboardBaseView implements PopupPanel {
private final int[] mCoordinates = new int[2];
private final boolean mConfigShowMiniKeyboardAtTouchedPoint;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
index 3e433361a..e35db8955 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
@@ -236,10 +236,10 @@ public class KeyboardParser {
R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5));
mKeyboard.mIconsSet.loadIcons(keyboardAttr);
- mKeyboardTopPadding = keyboardAttr.getDimensionPixelSize(
- R.styleable.Keyboard_keyboardTopPadding, 0);
- mKeyboardBottomPadding = keyboardAttr.getDimensionPixelSize(
- R.styleable.Keyboard_keyboardBottomPadding, 0);
+ mKeyboardTopPadding = getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_keyboardTopPadding, height, 0);
+ mKeyboardBottomPadding = getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_keyboardBottomPadding, height, 0);
} finally {
keyAttr.recycle();
keyboardAttr.recycle();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
index 1e67eec70..1885ea14e 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
@@ -23,7 +23,7 @@ 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.LatinKeyboardBaseView;
import com.android.inputmethod.keyboard.MiniKeyboard;
import com.android.inputmethod.latin.R;
@@ -43,6 +43,7 @@ public class MiniKeyboardBuilder {
public final int mNumColumns;
public final int mLeftKeys;
public final int mRightKeys; // includes default key.
+ public int mTopPadding;
/**
* The object holding mini keyboard layout parameters.
@@ -53,6 +54,7 @@ public class MiniKeyboardBuilder {
* @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.
+ * parent keyboard.
*/
public MiniKeyboardLayoutParams(int numKeys, int maxColumns, int keyWidth, int rowHeight,
int coordXInParent, int parentKeyboardWidth) {
@@ -170,7 +172,7 @@ public class MiniKeyboardBuilder {
}
public int getY(int row) {
- return (mNumRows - 1 - row) * mRowHeight;
+ return (mNumRows - 1 - row) * mRowHeight + mTopPadding;
}
public int getRowFlags(int row) {
@@ -183,9 +185,21 @@ public class MiniKeyboardBuilder {
private boolean isTopRow(int rowCount) {
return rowCount == mNumRows - 1;
}
+
+ public void setTopPadding (int topPadding) {
+ mTopPadding = topPadding;
+ }
+
+ public int getKeyboardHeight() {
+ return mNumRows * mRowHeight + mTopPadding;
+ }
+
+ public int getKeyboardWidth() {
+ return mNumColumns * mKeyWidth;
+ }
}
- public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey,
+ public MiniKeyboardBuilder(LatinKeyboardBaseView view, int layoutTemplateResId, Key parentKey,
Keyboard parentKeyboard) {
final Context context = view.getContext();
mRes = context.getResources();
@@ -200,15 +214,16 @@ public class MiniKeyboardBuilder {
keyWidth, parentKeyboard.getRowHeight(),
parentKey.mX + (parentKey.mWidth + parentKey.mGap) / 2 - keyWidth / 2,
view.getMeasuredWidth());
+ params.setTopPadding(keyboard.getVerticalGap());
mParams = params;
keyboard.setRowHeight(params.mRowHeight);
- keyboard.setHeight(params.mNumRows * params.mRowHeight);
- keyboard.setMinWidth(params.mNumColumns * params.mKeyWidth);
+ keyboard.setHeight(params.getKeyboardHeight());
+ keyboard.setMinWidth(params.getKeyboardWidth());
keyboard.setDefaultCoordX(params.getDefaultKeyCoordX() + params.mKeyWidth / 2);
}
- private static int getMaxKeyWidth(KeyboardView view, CharSequence[] popupCharacters,
+ private static int getMaxKeyWidth(LatinKeyboardBaseView view, CharSequence[] popupCharacters,
int minKeyWidth) {
Paint paint = null;
Rect bounds = null;
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index a5bfea0f8..7ec18b38a 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -21,6 +21,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
import android.os.Message;
import android.text.Spannable;
import android.text.SpannableString;
@@ -30,6 +31,7 @@ import android.text.TextUtils;
import android.text.style.BackgroundColorSpan;
import android.text.style.CharacterStyle;
import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
import android.text.style.UnderlineSpan;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -38,7 +40,6 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
@@ -57,24 +58,29 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
public void pickSuggestionManually(int index, CharSequence word);
}
+ private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
// The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
private static final int MAX_SUGGESTIONS = 18;
- private static final int MATCH_PARENT = MeasureSpec.makeMeasureSpec(
- -1, MeasureSpec.UNSPECIFIED);
+ private static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
+ private static final int MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT;
private static final boolean DBG = LatinImeLogger.sDBG;
- private final View mCandidatesStrip;
- private static final int NUM_CANDIDATES_IN_STRIP = 3;
- private final ImageView mExpandCandidatesPane;
- private final ImageView mCloseCandidatesPane;
+ private final ViewGroup mCandidatesStrip;
+ private final int mCandidateCountInStrip;
+ private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3;
+ private final ViewGroup mCandidatesPaneControl;
+ private final TextView mExpandCandidatesPane;
+ private final TextView mCloseCandidatesPane;
private ViewGroup mCandidatesPane;
private ViewGroup mCandidatesPaneContainer;
private View mKeyboardView;
+
private final ArrayList<TextView> mWords = new ArrayList<TextView>();
private final ArrayList<TextView> mInfos = new ArrayList<TextView>();
private final ArrayList<View> mDividers = new ArrayList<View>();
+
private final int mCandidateStripHeight;
private final CharacterStyle mInvertedForegroundColorSpan;
private final CharacterStyle mInvertedBackgroundColorSpan;
@@ -85,6 +91,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
private final int mColorTypedWord;
private final int mColorAutoCorrect;
private final int mColorSuggestedCandidate;
+
private final PopupWindow mPreviewPopup;
private final TextView mPreviewText;
@@ -96,8 +103,9 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
private boolean mShowingAutoCorrectionInverted;
private boolean mShowingAddToDictionary;
- private static final float MIN_TEXT_XSCALE = 0.4f;
- private static final String ELLIPSIS = "\u2026";
+ private final CandidateViewLayoutParams mParams;
+ private static final int PUNCTUATIONS_IN_STRIP = 6;
+ private static final float MIN_TEXT_XSCALE = 0.75f;
private final UiHandler mHandler = new UiHandler(this);
@@ -150,6 +158,115 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
}
+ private static class CandidateViewLayoutParams {
+ public final TextPaint mPaint;
+ public final int mPadding;
+ public final int mDividerWidth;
+ public final int mDividerHeight;
+ public final int mControlWidth;
+ private final int mAutoCorrectHighlight;
+
+ public final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
+
+ public int mCountInStrip;
+ // True if the mCountInStrip suggestions can fit in suggestion strip in equally divided
+ // width without squeezing the text.
+ public boolean mCanUseFixedWidthColumns;
+ public int mMaxWidth;
+ public int mAvailableWidthForWords;
+ public int mConstantWidthForPaddings;
+ public int mVariableWidthForWords;
+ public float mScaleX;
+
+ public CandidateViewLayoutParams(Resources res, TextView word, View divider, View control,
+ int autoCorrectHighlight) {
+ mPaint = new TextPaint();
+ final float textSize = res.getDimension(R.dimen.candidate_text_size);
+ mPaint.setTextSize(textSize);
+ mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight();
+ divider.measure(WRAP_CONTENT, MATCH_PARENT);
+ mDividerWidth = divider.getMeasuredWidth();
+ mDividerHeight = divider.getMeasuredHeight();
+ mControlWidth = control.getMeasuredWidth();
+ mAutoCorrectHighlight = autoCorrectHighlight;
+ }
+
+ public void layoutStrip(SuggestedWords suggestions, int maxWidth, int maxCount) {
+ final int size = suggestions.size();
+ setupTexts(suggestions, size, mAutoCorrectHighlight);
+ mCountInStrip = Math.min(maxCount, size);
+ mScaleX = 1.0f;
+
+ do {
+ mMaxWidth = maxWidth;
+ if (size > mCountInStrip) {
+ mMaxWidth -= mControlWidth;
+ }
+
+ tryLayout();
+
+ if (mCanUseFixedWidthColumns) {
+ return;
+ }
+ if (mVariableWidthForWords <= mAvailableWidthForWords) {
+ return;
+ }
+
+ final float scaleX = mAvailableWidthForWords / (float)mVariableWidthForWords;
+ if (scaleX >= MIN_TEXT_XSCALE) {
+ mScaleX = scaleX;
+ return;
+ }
+
+ mCountInStrip--;
+ } while (mCountInStrip > 1);
+ }
+
+ public void tryLayout() {
+ final int maxCount = mCountInStrip;
+ final int dividers = mDividerWidth * (maxCount - 1);
+ mConstantWidthForPaddings = dividers + mPadding * maxCount;
+ mAvailableWidthForWords = mMaxWidth - mConstantWidthForPaddings;
+
+ mPaint.setTextScaleX(mScaleX);
+ final int maxFixedWidthForWord = (mMaxWidth - dividers) / maxCount - mPadding;
+ mCanUseFixedWidthColumns = true;
+ mVariableWidthForWords = 0;
+ for (int i = 0; i < maxCount; i++) {
+ final int width = getTextWidth(mTexts.get(i), mPaint);
+ if (width > maxFixedWidthForWord)
+ mCanUseFixedWidthColumns = false;
+ mVariableWidthForWords += width;
+ }
+ }
+
+ private void setupTexts(SuggestedWords suggestions, int count, int autoCorrectHighlight) {
+ mTexts.clear();
+ for (int i = 0; i < count; i++) {
+ final CharSequence suggestion = suggestions.getWord(i);
+ if (suggestion == null) continue;
+
+ final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion
+ && ((i == 1 && !suggestions.mTypedWordValid)
+ || (i == 0 && suggestions.mTypedWordValid));
+ // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1
+ // and there are multiple suggestions, such as the default punctuation list.
+ // TODO: Need to revisit this logic with bigram suggestions
+ final CharSequence styled = getStyledCandidateWord(suggestion, isAutoCorrect,
+ autoCorrectHighlight);
+ mTexts.add(styled);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "count=%d width=%d avail=%d fixcol=%s scaleX=%4.2f const=%d var=%d",
+ mCountInStrip, mMaxWidth, mAvailableWidthForWords, mCanUseFixedWidthColumns,
+ mScaleX, mConstantWidthForPaddings, mVariableWidthForWords);
+ }
+ }
+
/**
* Construct a CandidateView for showing suggested words for completion.
* @param context
@@ -173,6 +290,16 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
setBackgroundDrawable(LinearLayoutCompatUtils.getBackgroundDrawable(
context, attrs, defStyle, R.style.CandidateViewStyle));
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle);
+ mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0);
+ mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0);
+ mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0);
+ mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0);
+ mCandidateCountInStrip = a.getInt(
+ R.styleable.CandidateView_candidateCountInStrip, DEFAULT_CANDIDATE_COUNT_IN_STRIP);
+ a.recycle();
+
Resources res = context.getResources();
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.candidates_strip, this);
@@ -184,74 +311,57 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
mPreviewPopup.setContentView(mPreviewText);
mPreviewPopup.setBackgroundDrawable(null);
- mCandidatesStrip = findViewById(R.id.candidates_strip);
+ mCandidatesStrip = (ViewGroup)findViewById(R.id.candidates_strip);
mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height);
for (int i = 0; i < MAX_SUGGESTIONS; i++) {
- final TextView word, info;
- switch (i) {
- case 0:
- word = (TextView)findViewById(R.id.word_left);
- info = (TextView)findViewById(R.id.info_left);
- break;
- case 1:
- word = (TextView)findViewById(R.id.word_center);
- info = (TextView)findViewById(R.id.info_center);
- break;
- case 2:
- word = (TextView)findViewById(R.id.word_right);
- info = (TextView)findViewById(R.id.info_right);
- break;
- default:
- word = (TextView)inflater.inflate(R.layout.candidate_word, null);
- info = (TextView)inflater.inflate(R.layout.candidate_info, null);
- break;
- }
+ final TextView word = (TextView)inflater.inflate(R.layout.candidate_word, null);
word.setTag(i);
word.setOnClickListener(this);
if (i == 0)
word.setOnLongClickListener(this);
mWords.add(word);
- mInfos.add(info);
- if (i > 0) {
- final View divider = inflater.inflate(R.layout.candidate_divider, null);
- divider.measure(MATCH_PARENT, MATCH_PARENT);
- mDividers.add(divider);
- }
+ mInfos.add((TextView)inflater.inflate(R.layout.candidate_info, null));
+ mDividers.add(inflater.inflate(R.layout.candidate_divider, null));
}
mTouchToSave = findViewById(R.id.touch_to_save);
mWordToSave = (TextView)findViewById(R.id.word_to_save);
mWordToSave.setOnClickListener(this);
- final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle);
- mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0);
- mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0);
- mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0);
- mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0);
mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff);
mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord);
- mExpandCandidatesPane = (ImageView)findViewById(R.id.expand_candidates_pane);
- mExpandCandidatesPane.setImageDrawable(
- a.getDrawable(R.styleable.CandidateView_iconExpandPane));
+ final TypedArray keyboardViewAttr = context.obtainStyledAttributes(
+ attrs, R.styleable.KeyboardView, R.attr.keyboardViewStyle, R.style.KeyboardView);
+ final Drawable keyBackground = keyboardViewAttr.getDrawable(
+ R.styleable.KeyboardView_keyBackground);
+ final int keyTextColor = keyboardViewAttr.getColor(
+ R.styleable.KeyboardView_keyTextColor, 0xFF000000);
+ keyboardViewAttr.recycle();
+
+ mCandidatesPaneControl = (ViewGroup)findViewById(R.id.candidates_pane_control);
+ mExpandCandidatesPane = (TextView)findViewById(R.id.expand_candidates_pane);
+ mExpandCandidatesPane.setBackgroundDrawable(keyBackground);
+ mExpandCandidatesPane.setTextColor(keyTextColor);
mExpandCandidatesPane.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
expandCandidatesPane();
}
});
- mCloseCandidatesPane = (ImageView)findViewById(R.id.close_candidates_pane);
- mCloseCandidatesPane.setImageDrawable(
- a.getDrawable(R.styleable.CandidateView_iconClosePane));
+ mCloseCandidatesPane = (TextView)findViewById(R.id.close_candidates_pane);
+ mCloseCandidatesPane.setBackgroundDrawable(keyBackground);
+ mCloseCandidatesPane.setTextColor(keyTextColor);
mCloseCandidatesPane.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
closeCandidatesPane();
}
});
+ mCandidatesPaneControl.measure(WRAP_CONTENT, WRAP_CONTENT);
- a.recycle();
+ mParams = new CandidateViewLayoutParams(res,
+ mWords.get(0), mDividers.get(0), mCandidatesPaneControl, mAutoCorrectHighlight);
}
/**
@@ -272,6 +382,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
if (suggestions == null)
return;
mSuggestions = suggestions;
+ mExpandCandidatesPane.setEnabled(false);
if (mShowingAutoCorrectionInverted) {
mHandler.postUpdateSuggestions();
} else {
@@ -279,15 +390,14 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
}
- private CharSequence getStyledCandidateWord(CharSequence word, TextView v,
- boolean isAutoCorrect) {
- v.setTypeface(Typeface.DEFAULT);
+ private static CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect,
+ int autoCorrectHighlight) {
if (!isAutoCorrect)
return word;
final Spannable spannedWord = new SpannableString(word);
- if ((mAutoCorrectHighlight & AUTO_CORRECT_BOLD) != 0)
- v.setTypeface(Typeface.DEFAULT_BOLD);
- if ((mAutoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0)
+ if ((autoCorrectHighlight & AUTO_CORRECT_BOLD) != 0)
+ spannedWord.setSpan(BOLD_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ if ((autoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0)
spannedWord.setSpan(UNDERLINE_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
return spannedWord;
}
@@ -313,113 +423,164 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
private void updateSuggestions() {
final SuggestedWords suggestions = mSuggestions;
final List<SuggestedWordInfo> suggestedWordInfoList = suggestions.mSuggestedWordInfoList;
+ final int paneWidth = getWidth();
+ final CandidateViewLayoutParams params = mParams;
clear();
- final int paneWidth = getWidth();
- final int dividerWidth = mDividers.get(0).getMeasuredWidth();
- final int dividerHeight = mDividers.get(0).getMeasuredHeight();
- int x = 0;
- int y = 0;
- int fromIndex = NUM_CANDIDATES_IN_STRIP;
- final int count = Math.min(mWords.size(), suggestions.size());
closeCandidatesPane();
- mExpandCandidatesPane.setVisibility(count > NUM_CANDIDATES_IN_STRIP ? VISIBLE : GONE);
+ if (suggestions.size() == 0)
+ return;
+
+ params.layoutStrip(suggestions, paneWidth, suggestions.isPunctuationSuggestions()
+ ? PUNCTUATIONS_IN_STRIP : mCandidateCountInStrip);
+
+ final int count = Math.min(mWords.size(), suggestions.size());
+ if (count <= params.mCountInStrip && !DBG) {
+ mCandidatesPaneControl.setVisibility(GONE);
+ } else {
+ mCandidatesPaneControl.setVisibility(VISIBLE);
+ mExpandCandidatesPane.setVisibility(VISIBLE);
+ mExpandCandidatesPane.setEnabled(true);
+ }
+
+ final int countInStrip = params.mCountInStrip;
+ View centeringFrom = null, lastView = null;
+ int x = 0, y = 0, infoX = 0;
for (int i = 0; i < count; i++) {
- final CharSequence suggestion = suggestions.getWord(i);
+ final int pos;
+ if (i <= 1) {
+ final boolean willAutoCorrect = !suggestions.mTypedWordValid
+ && suggestions.mHasMinimalSuggestion;
+ pos = willAutoCorrect ? 1 - i : i;
+ } else {
+ pos = i;
+ }
+ final CharSequence suggestion = suggestions.getWord(pos);
if (suggestion == null) continue;
final SuggestedWordInfo suggestionInfo = (suggestedWordInfoList != null)
- ? suggestedWordInfoList.get(i) : null;
+ ? suggestedWordInfoList.get(pos) : null;
final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion
- && ((i == 1 && !suggestions.mTypedWordValid)
- || (i == 0 && suggestions.mTypedWordValid));
+ && ((pos == 1 && !suggestions.mTypedWordValid)
+ || (pos == 0 && suggestions.mTypedWordValid));
// HACK: even if i == 0, we use mColorOther when this suggestion's length is 1
// and there are multiple suggestions, such as the default punctuation list.
// TODO: Need to revisit this logic with bigram suggestions
- final boolean isSuggestedCandidate = (i != 0);
+ final boolean isSuggestedCandidate = (pos != 0);
final boolean isPunctuationSuggestions = (suggestion.length() == 1 && count > 1);
- final TextView word = mWords.get(i);
+ final TextView word = mWords.get(pos);
+ final TextPaint paint = word.getPaint();
// TODO: Reorder candidates in strip as appropriate. The center candidate should hold
// the word when space is typed (valid typed word or auto corrected word).
word.setTextColor(getCandidateTextColor(isAutoCorrect,
isSuggestedCandidate || isPunctuationSuggestions, suggestionInfo));
- final CharSequence text = getStyledCandidateWord(suggestion, word, isAutoCorrect);
- if (i < NUM_CANDIDATES_IN_STRIP) {
- final View parent = (View)word.getParent();
- final int width = parent.getWidth() - word.getPaddingLeft()
- - word.getPaddingRight();
- setTextWithAutoScaleAndEllipsis(text, width, word);
- } else {
- setTextWithAutoScaleAndEllipsis(text, paneWidth, word);
- }
+ final CharSequence styled = params.mTexts.get(pos);
final TextView info;
if (DBG && suggestionInfo != null
&& !TextUtils.isEmpty(suggestionInfo.getDebugString())) {
info = mInfos.get(i);
info.setText(suggestionInfo.getDebugString());
- info.setVisibility(View.VISIBLE);
} else {
info = null;
}
- if (i < NUM_CANDIDATES_IN_STRIP) {
+ final CharSequence text;
+ final float scaleX;
+ if (i < countInStrip) {
+ if (i == 0 && params.mCountInStrip == 1) {
+ text = getEllipsizedText(styled, params.mMaxWidth, paint);
+ scaleX = paint.getTextScaleX();
+ } else {
+ text = styled;
+ scaleX = params.mScaleX;
+ }
+ word.setText(text);
+ word.setTextScaleX(scaleX);
+ if (i != 0) {
+ // Add divider if this isn't the left most suggestion in candidate strip.
+ mCandidatesStrip.addView(mDividers.get(i));
+ }
+ mCandidatesStrip.addView(word);
+ if (params.mCanUseFixedWidthColumns) {
+ setLayoutWeight(word, 1.0f, mCandidateStripHeight);
+ } else {
+ final int width = getTextWidth(text, paint) + params.mPadding;
+ setLayoutWeight(word, width, mCandidateStripHeight);
+ }
if (info != null) {
- word.measure(MATCH_PARENT, MATCH_PARENT);
- info.measure(MATCH_PARENT, MATCH_PARENT);
- final int width = word.getMeasuredWidth();
- final int infoWidth = info.getMeasuredWidth();
- FrameLayoutCompatUtils.placeViewAt(
- info, width - infoWidth, 0, infoWidth, info.getMeasuredHeight());
+ mCandidatesPane.addView(info);
+ info.measure(WRAP_CONTENT, WRAP_CONTENT);
+ final int width = info.getMeasuredWidth();
+ y = info.getMeasuredHeight();
+ FrameLayoutCompatUtils.placeViewAt(info, infoX, 0, width, y);
+ infoX += width * 2;
}
} else {
- word.measure(MATCH_PARENT, MATCH_PARENT);
- final int width = word.getMeasuredWidth();
- final int height = word.getMeasuredHeight();
- // TODO: Handle overflow case.
- if (dividerWidth + x + width >= paneWidth) {
- centeringCandidates(fromIndex, i - 1, x, paneWidth);
+ paint.setTextScaleX(1.0f);
+ final int textWidth = getTextWidth(styled, paint);
+ int available = paneWidth - x - params.mPadding;
+ if (textWidth >= available) {
+ // Needs new row, centering previous row.
+ centeringCandidates(centeringFrom, lastView, x, paneWidth);
x = 0;
y += mCandidateStripHeight;
- fromIndex = i;
}
if (x != 0) {
- final View divider = mDividers.get(i - NUM_CANDIDATES_IN_STRIP);
+ // Add divider if this isn't the left most suggestion in current row.
+ final View divider = mDividers.get(i);
mCandidatesPane.addView(divider);
FrameLayoutCompatUtils.placeViewAt(
- divider, x, y + (mCandidateStripHeight - dividerHeight) / 2,
- dividerWidth, dividerHeight);
- x += dividerWidth;
+ divider, x, y + (mCandidateStripHeight - params.mDividerHeight) / 2,
+ params.mDividerWidth, params.mDividerHeight);
+ x += params.mDividerWidth;
}
+ available = paneWidth - x - params.mPadding;
+ text = getEllipsizedText(styled, available, paint);
+ scaleX = paint.getTextScaleX();
+ word.setText(text);
+ word.setTextScaleX(scaleX);
mCandidatesPane.addView(word);
+ lastView = word;
+ if (x == 0) centeringFrom = word;
+ word.measure(WRAP_CONTENT,
+ MeasureSpec.makeMeasureSpec(mCandidateStripHeight, MeasureSpec.EXACTLY));
+ final int width = word.getMeasuredWidth();
+ final int height = word.getMeasuredHeight();
FrameLayoutCompatUtils.placeViewAt(
word, x, y + (mCandidateStripHeight - height) / 2, width, height);
+ x += width;
if (info != null) {
mCandidatesPane.addView(info);
- info.measure(MATCH_PARENT, MATCH_PARENT);
+ lastView = info;
+ info.measure(WRAP_CONTENT, WRAP_CONTENT);
final int infoWidth = info.getMeasuredWidth();
FrameLayoutCompatUtils.placeViewAt(
- info, x + width - infoWidth, y, infoWidth, info.getMeasuredHeight());
+ info, x - infoWidth, y, infoWidth, info.getMeasuredHeight());
}
- x += width;
}
}
if (x != 0) {
// Centering last candidates row.
- centeringCandidates(fromIndex, count - 1, x, paneWidth);
+ centeringCandidates(centeringFrom, lastView, x, paneWidth);
}
}
- private void centeringCandidates(int from, int to, int width, int paneWidth) {
- final ViewGroup pane = mCandidatesPane;
- final int fromIndex = pane.indexOfChild(mWords.get(from));
- final int toIndex;
- if (mInfos.get(to).getParent() != null) {
- toIndex = pane.indexOfChild(mInfos.get(to));
- } else {
- toIndex = pane.indexOfChild(mWords.get(to));
+ private static void setLayoutWeight(View v, float weight, int height) {
+ final ViewGroup.LayoutParams lp = v.getLayoutParams();
+ if (lp instanceof LinearLayout.LayoutParams) {
+ final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp;
+ llp.weight = weight;
+ llp.width = 0;
+ llp.height = height;
}
+ }
+
+ private void centeringCandidates(View from, View to, int width, int paneWidth) {
+ final ViewGroup pane = mCandidatesPane;
+ final int fromIndex = pane.indexOfChild(from);
+ final int toIndex = pane.indexOfChild(to);
final int offset = (paneWidth - width) / 2;
for (int index = fromIndex; index <= toIndex; index++) {
offsetMargin(pane.getChildAt(index), offset, 0);
@@ -436,73 +597,68 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
}
- private static void setTextWithAutoScaleAndEllipsis(CharSequence text, int w, TextView v) {
- // To prevent partially rendered character at the end of text, subtract few extra pixels
- // from the width.
- final int width = w - 4;
-
- final TextPaint paint = v.getPaint();
- final int textWidth = getTextWidth(text, paint, 1.0f);
- if (textWidth < width || textWidth == 0 || width <= 0) {
- v.setTextScaleX(1.0f);
- v.setText(text);
- return;
- }
-
- final float scaleX = Math.min((float)width / textWidth, 1.0f);
+ private static CharSequence getEllipsizedText(CharSequence text, int maxWidth,
+ TextPaint paint) {
+ paint.setTextScaleX(1.0f);
+ final int width = getTextWidth(text, paint);
+ final float scaleX = Math.min(maxWidth / (float)width, 1.0f);
if (scaleX >= MIN_TEXT_XSCALE) {
- v.setTextScaleX(scaleX);
- v.setText(text);
- return;
+ paint.setTextScaleX(scaleX);
+ return text;
}
- final int truncatedWidth = width - getTextWidth(ELLIPSIS, paint, MIN_TEXT_XSCALE);
- final CharSequence ellipsized = getTextEllipsizedAtStart(text, paint, truncatedWidth);
- v.setTextScaleX(MIN_TEXT_XSCALE);
- v.setText(ELLIPSIS);
- v.append(ellipsized);
+ // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To get
+ // squeezed and ellipsezed text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
+ final CharSequence ellipsized = TextUtils.ellipsize(
+ text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE);
+ paint.setTextScaleX(MIN_TEXT_XSCALE);
+ return ellipsized;
}
- private static int getTextWidth(CharSequence text, TextPaint paint, float scaleX) {
+ private static int getTextWidth(CharSequence text, TextPaint paint) {
if (TextUtils.isEmpty(text)) return 0;
+ final Typeface savedTypeface = paint.getTypeface();
+ paint.setTypeface(getTextTypeface(text));
final int len = text.length();
final float[] widths = new float[len];
- paint.setTextScaleX(scaleX);
final int count = paint.getTextWidths(text, 0, len, widths);
- float width = 0;
+ int width = 0;
for (int i = 0; i < count; i++) {
- width += widths[i];
+ width += Math.round(widths[i] + 0.5f);
}
- return (int)Math.round(width + 0.5);
+ paint.setTypeface(savedTypeface);
+ return width;
}
- private static CharSequence getTextEllipsizedAtStart(CharSequence text, TextPaint paint,
- int maxWidth) {
- final int len = text.length();
- final float[] widths = new float[len];
- final int count = paint.getTextWidths(text, 0, len, widths);
- float width = 0;
- for (int i = count - 1; i >= 0; i--) {
- width += widths[i];
- if (width > maxWidth)
- return text.subSequence(i + 1, len);
+ private static Typeface getTextTypeface(CharSequence text) {
+ if (!(text instanceof SpannableString))
+ return Typeface.DEFAULT;
+
+ final SpannableString ss = (SpannableString)text;
+ final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class);
+ if (styles.length == 0)
+ return Typeface.DEFAULT;
+
+ switch (styles[0].getStyle()) {
+ case Typeface.BOLD: return Typeface.DEFAULT_BOLD;
+ // TODO: BOLD_ITALIC, ITALIC case?
+ default: return Typeface.DEFAULT;
}
- return text;
}
private void expandCandidatesPane() {
- mExpandCandidatesPane.setVisibility(View.GONE);
- mCloseCandidatesPane.setVisibility(View.VISIBLE);
+ mExpandCandidatesPane.setVisibility(GONE);
+ mCloseCandidatesPane.setVisibility(VISIBLE);
mCandidatesPaneContainer.setMinimumHeight(mKeyboardView.getMeasuredHeight());
- mCandidatesPaneContainer.setVisibility(View.VISIBLE);
- mKeyboardView.setVisibility(View.GONE);
+ mCandidatesPaneContainer.setVisibility(VISIBLE);
+ mKeyboardView.setVisibility(GONE);
}
private void closeCandidatesPane() {
- mExpandCandidatesPane.setVisibility(View.VISIBLE);
- mCloseCandidatesPane.setVisibility(View.GONE);
- mCandidatesPaneContainer.setVisibility(View.GONE);
- mKeyboardView.setVisibility(View.VISIBLE);
+ mExpandCandidatesPane.setVisibility(VISIBLE);
+ mCloseCandidatesPane.setVisibility(GONE);
+ mCandidatesPaneContainer.setVisibility(GONE);
+ mKeyboardView.setVisibility(VISIBLE);
}
public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) {
@@ -526,8 +682,9 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
public void showAddToDictionaryHint(CharSequence word) {
mWordToSave.setText(word);
mShowingAddToDictionary = true;
- mCandidatesStrip.setVisibility(View.GONE);
- mTouchToSave.setVisibility(View.VISIBLE);
+ mCandidatesStrip.setVisibility(GONE);
+ mCandidatesPaneControl.setVisibility(GONE);
+ mTouchToSave.setVisibility(VISIBLE);
}
public boolean dismissAddToDictionaryHint() {
@@ -543,12 +700,9 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
public void clear() {
mShowingAddToDictionary = false;
mShowingAutoCorrectionInverted = false;
- for (int i = 0; i < NUM_CANDIDATES_IN_STRIP; i++) {
- mWords.get(i).setText(null);
- mInfos.get(i).setVisibility(View.GONE);
- }
- mTouchToSave.setVisibility(View.GONE);
- mCandidatesStrip.setVisibility(View.VISIBLE);
+ mTouchToSave.setVisibility(GONE);
+ mCandidatesStrip.setVisibility(VISIBLE);
+ mCandidatesStrip.removeAllViews();
mCandidatesPane.removeAllViews();
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index a83aca0a2..4a813541d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -66,7 +66,7 @@ import com.android.inputmethod.deprecated.recorrection.Recorrection;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.LatinKeyboardBaseView;
import com.android.inputmethod.keyboard.LatinKeyboard;
import com.android.inputmethod.keyboard.LatinKeyboardView;
@@ -655,7 +655,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
@Override
public void onWindowHidden() {
super.onWindowHidden();
- KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ LatinKeyboardBaseView inputView = mKeyboardSwitcher.getKeyboardView();
if (inputView != null) inputView.closing();
}
@@ -668,7 +668,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging);
- KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ LatinKeyboardBaseView inputView = mKeyboardSwitcher.getKeyboardView();
if (inputView != null) inputView.closing();
if (mAutoDictionary != null) mAutoDictionary.flushPendingWrites();
if (mUserBigramDictionary != null) mUserBigramDictionary.flushPendingWrites();
@@ -677,8 +677,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
@Override
public void onFinishInputView(boolean finishingInput) {
super.onFinishInputView(finishingInput);
- KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView != null) inputView.cancelAllMessage();
+ LatinKeyboardBaseView inputView = mKeyboardSwitcher.getKeyboardView();
+ if (inputView != null) inputView.cancelAllMessages();
// Remove pending messages related to update suggestions
mHandler.cancelUpdateSuggestions();
mHandler.cancelUpdateOldSuggestions();
@@ -831,8 +831,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
SuggestedWords.Builder builder = new SuggestedWords.Builder()
.setApplicationSpecifiedCompletions(applicationSpecifiedCompletions)
- .setTypedWordValid(true)
- .setHasMinimalSuggestion(true);
+ .setTypedWordValid(false)
+ .setHasMinimalSuggestion(false);
// When in fullscreen mode, show completions generated by the application
setSuggestions(builder.build());
mBestWord = null;
@@ -866,7 +866,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
@Override
public void onComputeInsets(InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
- final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
+ final LatinKeyboardBaseView inputView = mKeyboardSwitcher.getKeyboardView();
if (inputView == null || mCandidateViewContainer == null)
return;
final int containerHeight = mCandidateViewContainer.getHeight();
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index baad66d87..d6b989073 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -18,14 +18,14 @@ package com.android.inputmethod.latin;
import com.android.inputmethod.compat.CompatUtils;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
-import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
import com.android.inputmethod.deprecated.VoiceProxy;
import com.android.inputmethod.compat.VibratorCompatWrapper;
+import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.Fragment;
import android.app.backup.BackupManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -40,7 +40,6 @@ import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.speech.SpeechRecognizer;
-import android.text.AutoText;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.util.Log;
@@ -288,7 +287,7 @@ public class Settings extends PreferenceActivity
builder.addWord(puncs.subSequence(i, i + 1));
}
}
- return builder.build();
+ return builder.setIsPunctuationSuggestions().build();
}
}
@@ -321,10 +320,22 @@ public class Settings extends PreferenceActivity
mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff));
}
+ public Activity getActivityInternal() {
+ Object thisObject = (Object) this;
+ if (thisObject instanceof Activity) {
+ return (Activity) thisObject;
+ } else if (thisObject instanceof Fragment) {
+ return ((Fragment) thisObject).getActivity();
+ } else {
+ return null;
+ }
+ }
+
@Override
- protected void onCreate(Bundle icicle) {
+ public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Resources res = getResources();
+ final Context context = getActivityInternal();
addPreferencesFromResource(R.xml.prefs);
mInputLanguageSelection = (PreferenceScreen) findPreference(PREF_SUBTYPES);
@@ -340,7 +351,7 @@ public class Settings extends PreferenceActivity
mVoiceModeOff = getString(R.string.voice_mode_off);
mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
.equals(mVoiceModeOff));
- mVoiceLogger = VoiceProxy.VoiceLoggerWrapper.getInstance(this);
+ mVoiceLogger = VoiceProxy.VoiceLoggerWrapper.getInstance(context);
mAutoCorrectionThreshold = (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTIONS);
@@ -348,7 +359,8 @@ public class Settings extends PreferenceActivity
mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
if (mDebugSettingsPreference != null) {
final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN);
- debugSettingsIntent.setClassName(getPackageName(), DebugSettings.class.getName());
+ debugSettingsIntent.setClassName(
+ context.getPackageName(), DebugSettings.class.getName());
mDebugSettingsPreference.setIntent(debugSettingsIntent);
}
@@ -371,7 +383,7 @@ public class Settings extends PreferenceActivity
generalSettings.removePreference(mVoicePreference);
}
- if (!VibratorCompatWrapper.getInstance(this).hasVibrator()) {
+ if (!VibratorCompatWrapper.getInstance(context).hasVibrator()) {
generalSettings.removePreference(findPreference(PREF_VIBRATE_ON));
}
@@ -427,22 +439,17 @@ public class Settings extends PreferenceActivity
(PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY);
final Intent intent = dictionaryLink.getIntent();
- final int number = getPackageManager().queryIntentActivities(intent, 0).size();
+ final int number = context.getPackageManager().queryIntentActivities(intent, 0).size();
if (0 >= number) {
textCorrectionGroup.removePreference(dictionaryLink);
}
}
@Override
- protected void onResume() {
+ public void onResume() {
super.onResume();
- int autoTextSize = AutoText.getSize(getListView());
- if (autoTextSize < 1) {
- ((PreferenceGroup) findPreference(PREF_CORRECTION_SETTINGS_KEY))
- .removePreference(mQuickFixes);
- }
if (!VoiceProxy.VOICE_INSTALLED
- || !SpeechRecognizer.isRecognitionAvailable(this)) {
+ || !SpeechRecognizer.isRecognitionAvailable(getActivityInternal())) {
getPreferenceScreen().removePreference(mVoicePreference);
} else {
updateVoiceModeSummary();
@@ -453,7 +460,7 @@ public class Settings extends PreferenceActivity
}
@Override
- protected void onDestroy() {
+ public void onDestroy() {
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
this);
super.onDestroy();
@@ -461,7 +468,7 @@ public class Settings extends PreferenceActivity
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
- (new BackupManager(this)).dataChanged();
+ (new BackupManager(getActivityInternal())).dataChanged();
// If turning on voice input, show dialog
if (key.equals(PREF_VOICE_SETTINGS_KEY) && !mVoiceOn) {
if (!prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
@@ -488,8 +495,9 @@ public class Settings extends PreferenceActivity
public boolean onPreferenceClick(Preference pref) {
if (pref == mInputLanguageSelection) {
startActivity(CompatUtils.getInputLanguageSelectionIntent(
- Utils.getInputMethodId(InputMethodManagerCompatWrapper.getInstance(this),
- getApplicationInfo().packageName), 0));
+ Utils.getInputMethodId(
+ InputMethodManagerCompatWrapper.getInstance(getActivityInternal()),
+ getActivityInternal().getApplicationInfo().packageName), 0));
return true;
}
return false;
@@ -515,7 +523,7 @@ public class Settings extends PreferenceActivity
private void showVoiceConfirmation() {
mOkClicked = false;
- showDialog(VOICE_INPUT_CONFIRM_DIALOG);
+ getActivityInternal().showDialog(VOICE_INPUT_CONFIRM_DIALOG);
// Make URL in the dialog message clickable
if (mDialog != null) {
TextView textView = (TextView) mDialog.findViewById(android.R.id.message);
@@ -531,7 +539,6 @@ public class Settings extends PreferenceActivity
[mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
}
- @Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case VOICE_INPUT_CONFIRM_DIALOG:
@@ -548,7 +555,7 @@ public class Settings extends PreferenceActivity
updateVoicePreference();
}
};
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivityInternal())
.setTitle(R.string.voice_warning_title)
.setPositiveButton(android.R.string.ok, listener)
.setNegativeButton(android.R.string.cancel, listener);
diff --git a/java/src/com/android/inputmethod/latin/SettingsActivity.java b/java/src/com/android/inputmethod/latin/SettingsActivity.java
new file mode 100644
index 000000000..434d5d5ea
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/SettingsActivity.java
@@ -0,0 +1,6 @@
+package com.android.inputmethod.latin;
+
+import android.os.Bundle;
+
+public class SettingsActivity extends Settings {
+} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 6ca12c0c5..8fc19ae87 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -188,6 +188,8 @@ public class SubtypeSwitcher {
// TODO: Update an icon for shortcut IME
final Map<InputMethodInfoCompatWrapper, List<InputMethodSubtypeCompatWrapper>> shortcuts =
mImm.getShortcutInputMethodsAndSubtypes();
+ mShortcutInputMethodInfo = null;
+ mShortcutSubtype = null;
for (InputMethodInfoCompatWrapper imi : shortcuts.keySet()) {
List<InputMethodSubtypeCompatWrapper> subtypes = shortcuts.get(imi);
// TODO: Returns the first found IMI for now. Should handle all shortcuts as
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index a8cdfc02e..84db17504 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -24,15 +24,17 @@ import java.util.HashSet;
import java.util.List;
public class SuggestedWords {
- public static final SuggestedWords EMPTY = new SuggestedWords(null, false, false, null);
+ public static final SuggestedWords EMPTY = new SuggestedWords(null, false, false, false, null);
public final List<CharSequence> mWords;
public final boolean mTypedWordValid;
public final boolean mHasMinimalSuggestion;
+ public final boolean mIsPunctuationSuggestions;
public final List<SuggestedWordInfo> mSuggestedWordInfoList;
private SuggestedWords(List<CharSequence> words, boolean typedWordValid,
- boolean hasMinimalSuggestion, List<SuggestedWordInfo> suggestedWordInfoList) {
+ boolean hasMinimalSuggestion, boolean isPunctuationSuggestions,
+ List<SuggestedWordInfo> suggestedWordInfoList) {
if (words != null) {
mWords = words;
} else {
@@ -40,6 +42,7 @@ public class SuggestedWords {
}
mTypedWordValid = typedWordValid;
mHasMinimalSuggestion = hasMinimalSuggestion;
+ mIsPunctuationSuggestions = isPunctuationSuggestions;
mSuggestedWordInfoList = suggestedWordInfoList;
}
@@ -59,10 +62,15 @@ public class SuggestedWords {
return mHasMinimalSuggestion && ((size() > 1 && !mTypedWordValid) || mTypedWordValid);
}
+ public boolean isPunctuationSuggestions() {
+ return mIsPunctuationSuggestions;
+ }
+
public static class Builder {
private List<CharSequence> mWords = new ArrayList<CharSequence>();
private boolean mTypedWordValid;
private boolean mHasMinimalSuggestion;
+ private boolean mIsPunctuationSuggestions;
private List<SuggestedWordInfo> mSuggestedWordInfoList =
new ArrayList<SuggestedWordInfo>();
@@ -118,6 +126,11 @@ public class SuggestedWords {
return this;
}
+ public Builder setIsPunctuationSuggestions() {
+ mIsPunctuationSuggestions = true;
+ return this;
+ }
+
// Should get rid of the first one (what the user typed previously) from suggestions
// and replace it with what the user currently typed.
public Builder addTypedWordAndPreviousSuggestions(CharSequence typedWord,
@@ -143,7 +156,7 @@ public class SuggestedWords {
public SuggestedWords build() {
return new SuggestedWords(mWords, mTypedWordValid, mHasMinimalSuggestion,
- mSuggestedWordInfoList);
+ mIsPunctuationSuggestions, mSuggestedWordInfoList);
}
public int size() {