summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2010-11-18 14:20:30 -0800
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-11-18 14:20:30 -0800
commitdd4e4603efef2c3df0e26835935f74e1e0e7deb2 (patch)
tree2ba66f799846ce2707dad9e4c5bb98e50c1291c7 /core
parent2a26037ddd2d4c3150e3d8ca5d7caf23deae35d5 (diff)
parent6b53e8daa69cba1a2a5a7c95a01e37ce9c53226c (diff)
downloadframeworks_base-dd4e4603efef2c3df0e26835935f74e1e0e7deb2.zip
frameworks_base-dd4e4603efef2c3df0e26835935f74e1e0e7deb2.tar.gz
frameworks_base-dd4e4603efef2c3df0e26835935f74e1e0e7deb2.tar.bz2
Merge "Added support for full PC-style keyboards."
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/Instrumentation.java3
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java5
-rw-r--r--core/java/android/text/method/BaseKeyListener.java16
-rw-r--r--core/java/android/text/method/DialerKeyListener.java2
-rw-r--r--core/java/android/text/method/MetaKeyKeyListener.java104
-rw-r--r--core/java/android/text/method/NumberKeyListener.java2
-rw-r--r--core/java/android/text/method/QwertyKeyListener.java2
-rw-r--r--core/java/android/text/method/TextKeyListener.java5
-rw-r--r--core/java/android/text/method/Touch.java9
-rw-r--r--core/java/android/view/KeyCharacterMap.java524
-rwxr-xr-xcore/java/android/view/KeyEvent.java140
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java11
-rw-r--r--core/java/android/webkit/WebTextView.java3
-rw-r--r--core/java/android/webkit/WebView.java3
-rw-r--r--core/java/android/widget/DialerFilter.java12
-rw-r--r--core/java/android/widget/TextView.java7
-rw-r--r--core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java8
-rw-r--r--core/jni/android_text_KeyCharacterMap.cpp205
18 files changed, 634 insertions, 427 deletions
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 4d5f36a..1deb9fb 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -832,8 +832,7 @@ public class Instrumentation {
if (text == null) {
return;
}
- KeyCharacterMap keyCharacterMap =
- KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+ KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
KeyEvent[] events = keyCharacterMap.getEvents(text.toCharArray());
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8409baa..8150493 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -36,6 +36,7 @@ import android.text.method.MovementMethod;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Printer;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -1782,10 +1783,10 @@ public class InputMethodService extends AbstractInputMethodService {
if (ic == null) return;
long eventTime = SystemClock.uptimeMillis();
ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
- KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0,
+ KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
- KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0,
+ KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
}
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 6df6a3a..350c9a8 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -22,9 +22,15 @@ import android.text.*;
import android.text.method.TextKeyListener.Capitalize;
import android.widget.TextView;
-public abstract class BaseKeyListener
-extends MetaKeyKeyListener
-implements KeyListener {
+/**
+ * Abstract base class for key listeners.
+ *
+ * Provides a basic foundation for entering and editing text.
+ * Subclasses should override {@link #onKeyDown} and {@link #onKeyUp} to insert
+ * characters as keys are pressed.
+ */
+public abstract class BaseKeyListener extends MetaKeyKeyListener
+ implements KeyListener {
/* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete();
/**
@@ -34,7 +40,7 @@ implements KeyListener {
* if any;
* ALT+DEL deletes everything on the line the cursor is on.
*
- * @return true if anything was deleted; false otherwise.
+ * @return true if anything was deleted; false otherwise.
*/
public boolean backspace(View view, Editable content, int keyCode,
KeyEvent event) {
@@ -72,7 +78,7 @@ implements KeyListener {
private boolean altBackspace(View view, Editable content, int keyCode,
KeyEvent event) {
- if (getMetaState(content, META_ALT_ON) != 1) {
+ if (!event.isAltPressed() && getMetaState(content, META_ALT_ON) != 1) {
return false;
}
diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java
index 1e1812a..07127b7 100644
--- a/core/java/android/text/method/DialerKeyListener.java
+++ b/core/java/android/text/method/DialerKeyListener.java
@@ -49,7 +49,7 @@ public class DialerKeyListener extends NumberKeyListener
* from the KeyEvent.
*/
protected int lookup(KeyEvent event, Spannable content) {
- int meta = getMetaState(content);
+ int meta = event.getMetaState() | getMetaState(content);
int number = event.getNumber();
/*
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index e7b36d7..923a555 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -22,14 +22,54 @@ import android.text.Spannable;
import android.text.Spanned;
import android.view.KeyEvent;
import android.view.View;
+import android.view.KeyCharacterMap;
/**
- * This base class encapsulates the behavior for handling the meta keys
- * (shift and alt) and the pseudo-meta state of selecting text.
- * Key listeners that care about meta state should
- * inherit from it; you should not instantiate this class directly in a client.
+ * This base class encapsulates the behavior for tracking the state of
+ * meta keys such as SHIFT, ALT and SYM as well as the pseudo-meta state of selecting text.
+ * <p>
+ * Key listeners that care about meta state should inherit from this class;
+ * you should not instantiate this class directly in a client.
+ * </p><p>
+ * This class provides two mechanisms for tracking meta state that can be used
+ * together or independently.
+ * </p>
+ * <ul>
+ * <li>Methods such as {@link #handleKeyDown(long, int, KeyEvent)} and
+ * {@link #getMetaState(long)} operate on a meta key state bit mask.</li>
+ * <li>Methods such as {@link #onKeyDown(View, Editable, int, KeyEvent)} and
+ * {@link #getMetaState(CharSequence, int)} operate on meta key state flags stored
+ * as spans in an {@link Editable} text buffer. The spans only describe the current
+ * meta key state of the text editor; they do not carry any positional information.</li>
+ * </ul>
+ * <p>
+ * The behavior of this class varies according to the keyboard capabilities
+ * described by the {@link KeyCharacterMap} of the keyboard device such as
+ * the {@link KeyCharacterMap#getModifierBehavior() key modifier behavior}.
+ * </p><p>
+ * {@link MetaKeyKeyListener} implements chorded and toggled key modifiers.
+ * When key modifiers are toggled into a latched or locked state, the state
+ * of the modifier is stored in the {@link Editable} text buffer or in a
+ * meta state integer managed by the client. These latched or locked modifiers
+ * should be considered to be held <b>in addition to</b> those that the
+ * keyboard already reported as being pressed in {@link KeyEvent#getMetaState()}.
+ * In other words, the {@link MetaKeyKeyListener} augments the meta state
+ * provided by the keyboard; it does not replace it. This distinction is important
+ * to ensure that meta keys not handled by {@link MetaKeyKeyListener} such as
+ * {@link KeyEvent#KEYCODE_CAPS_LOCK} or {@link KeyEvent#KEYCODE_NUM_LOCK} are
+ * taken into consideration.
+ * </p><p>
+ * To ensure correct meta key behavior, the following pattern should be used
+ * when mapping key codes to characters:
+ * </p>
+ * <code>
+ * private char getUnicodeChar(TextKeyListener listener, KeyEvent event, Editable textBuffer) {
+ * // Use the combined meta states from the event and the key listener.
+ * int metaState = event.getMetaState() | listener.getMetaState(textBuffer);
+ * return event.getUnicodeChar(metaState);
+ * }
+ * </code>
*/
-
public abstract class MetaKeyKeyListener {
/**
* Flag that indicates that the SHIFT key is on.
@@ -227,8 +267,7 @@ public abstract class MetaKeyKeyListener {
/**
* Handles presses of the meta keys.
*/
- public boolean onKeyDown(View view, Editable content,
- int keyCode, KeyEvent event) {
+ public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
press(content, CAP);
return true;
@@ -283,34 +322,41 @@ public abstract class MetaKeyKeyListener {
/**
* Handles release of the meta keys.
*/
- public boolean onKeyUp(View view, Editable content, int keyCode,
- KeyEvent event) {
+ public boolean onKeyUp(View view, Editable content, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
- release(content, CAP);
+ release(content, CAP, event);
return true;
}
if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
|| keyCode == KeyEvent.KEYCODE_NUM) {
- release(content, ALT);
+ release(content, ALT, event);
return true;
}
if (keyCode == KeyEvent.KEYCODE_SYM) {
- release(content, SYM);
+ release(content, SYM, event);
return true;
}
return false; // no super to call through to
}
- private void release(Editable content, Object what) {
+ private void release(Editable content, Object what, KeyEvent event) {
int current = content.getSpanFlags(what);
- if (current == USED)
- content.removeSpan(what);
- else if (current == PRESSED)
- content.setSpan(what, 0, 0, RELEASED);
+ switch (event.getKeyCharacterMap().getModifierBehavior()) {
+ case KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED:
+ if (current == USED)
+ content.removeSpan(what);
+ else if (current == PRESSED)
+ content.setSpan(what, 0, 0, RELEASED);
+ break;
+
+ default:
+ content.removeSpan(what);
+ break;
+ }
}
public void clearMetaKeyState(View view, Editable content, int states) {
@@ -478,28 +524,36 @@ public abstract class MetaKeyKeyListener {
public static long handleKeyUp(long state, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
return release(state, META_SHIFT_ON, META_SHIFT_MASK,
- META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED);
+ META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED, event);
}
if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
|| keyCode == KeyEvent.KEYCODE_NUM) {
return release(state, META_ALT_ON, META_ALT_MASK,
- META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED);
+ META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED, event);
}
if (keyCode == KeyEvent.KEYCODE_SYM) {
return release(state, META_SYM_ON, META_SYM_MASK,
- META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED);
+ META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED, event);
}
return state;
}
private static long release(long state, int what, long mask,
- long pressed, long released, long used) {
- if ((state & used) != 0) {
- state &= ~mask;
- } else if ((state & pressed) != 0) {
- state |= what | released;
+ long pressed, long released, long used, KeyEvent event) {
+ switch (event.getKeyCharacterMap().getModifierBehavior()) {
+ case KeyCharacterMap.MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED:
+ if ((state & used) != 0) {
+ state &= ~mask;
+ } else if ((state & pressed) != 0) {
+ state |= what | released;
+ }
+ break;
+
+ default:
+ state &= ~mask;
+ break;
}
return state;
}
diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java
index 9270ca5..1e72309 100644
--- a/core/java/android/text/method/NumberKeyListener.java
+++ b/core/java/android/text/method/NumberKeyListener.java
@@ -37,7 +37,7 @@ public abstract class NumberKeyListener extends BaseKeyListener
protected abstract char[] getAcceptedChars();
protected int lookup(KeyEvent event, Spannable content) {
- return event.getMatch(getAcceptedChars(), getMetaState(content));
+ return event.getMatch(getAcceptedChars(), event.getMetaState() | getMetaState(content));
}
public CharSequence filter(CharSequence source, int start, int end,
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index b3926f2..3308172 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -83,7 +83,7 @@ public class QwertyKeyListener extends BaseKeyListener {
// QWERTY keyboard normal case
- int i = event.getUnicodeChar(getMetaState(content));
+ int i = event.getUnicodeChar(event.getMetaState() | getMetaState(content));
int count = event.getRepeatCount();
if (count > 0 && selStart == selEnd && selStart > 0) {
diff --git a/core/java/android/text/method/TextKeyListener.java b/core/java/android/text/method/TextKeyListener.java
index 09cbbb8..8ad6f50 100644
--- a/core/java/android/text/method/TextKeyListener.java
+++ b/core/java/android/text/method/TextKeyListener.java
@@ -180,13 +180,16 @@ public class TextKeyListener extends BaseKeyListener implements SpanWatcher {
}
private KeyListener getKeyListener(KeyEvent event) {
- KeyCharacterMap kmap = KeyCharacterMap.load(event.getKeyboardDevice());
+ KeyCharacterMap kmap = event.getKeyCharacterMap();
int kind = kmap.getKeyboardType();
if (kind == KeyCharacterMap.ALPHA) {
return QwertyKeyListener.getInstance(mAutoText, mAutoCap);
} else if (kind == KeyCharacterMap.NUMERIC) {
return MultiTapKeyListener.getInstance(mAutoText, mAutoCap);
+ } else if (kind == KeyCharacterMap.FULL
+ || kind == KeyCharacterMap.SPECIAL_FUNCTION) {
+ return QwertyKeyListener.getInstance(false, Capitalize.NONE);
}
return NullKeyListener.getInstance();
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index 9eaab17..78cbdcf 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -143,10 +143,11 @@ public class Touch {
if (ds[0].mFarEnough) {
ds[0].mUsed = true;
- boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SHIFT_ON) == 1) ||
- (MetaKeyKeyListener.getMetaState(buffer,
- MetaKeyKeyListener.META_SELECTING) != 0);
+ boolean cap = (event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0
+ || MetaKeyKeyListener.getMetaState(buffer,
+ MetaKeyKeyListener.META_SHIFT_ON) == 1
+ || MetaKeyKeyListener.getMetaState(buffer,
+ MetaKeyKeyListener.META_SELECTING) != 0;
float dx;
float dy;
if (cap) {
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 9981d87..fbd9eac 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -17,36 +17,86 @@
package android.view;
import android.text.method.MetaKeyKeyListener;
+import android.util.AndroidRuntimeException;
import android.util.SparseIntArray;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.util.SparseArray;
import java.lang.Character;
-import java.lang.ref.WeakReference;
/**
- * Describes the keys provided by a device and their associated labels.
+ * Describes the keys provided by a keyboard device and their associated labels.
*/
-public class KeyCharacterMap
-{
+public class KeyCharacterMap {
/**
* The id of the device's primary built in keyboard is always 0.
*/
public static final int BUILT_IN_KEYBOARD = 0;
- /** A numeric (12-key) keyboard. */
+ /**
+ * The id of a generic virtual keyboard with a full layout that can be used to
+ * synthesize key events. Typically used with {@link #getEvents}.
+ */
+ public static final int VIRTUAL_KEYBOARD = -1;
+
+ /**
+ * A numeric (12-key) keyboard.
+ * <p>
+ * A numeric keyboard supports text entry using a multi-tap approach.
+ * It may be necessary to tap a key multiple times to generate the desired letter
+ * or symbol.
+ * </p><p>
+ * This type of keyboard is generally designed for thumb typing.
+ * </p>
+ */
public static final int NUMERIC = 1;
- /** A keyboard with all the letters, but with more than one letter
- * per key. */
+ /**
+ * A keyboard with all the letters, but with more than one letter per key.
+ * <p>
+ * This type of keyboard is generally designed for thumb typing.
+ * </p>
+ */
public static final int PREDICTIVE = 2;
- /** A keyboard with all the letters, and maybe some numbers. */
+ /**
+ * A keyboard with all the letters, and maybe some numbers.
+ * <p>
+ * An alphabetic keyboard supports text entry directly but may have a condensed
+ * layout with a small form factor. In contrast to a {@link #FULL full keyboard}, some
+ * symbols may only be accessible using special on-screen character pickers.
+ * In addition, to improve typing speed and accuracy, the framework provides
+ * special affordances for alphabetic keyboards such as auto-capitalization
+ * and toggled / locked shift and alt keys.
+ * </p><p>
+ * This type of keyboard is generally designed for thumb typing.
+ * </p>
+ */
public static final int ALPHA = 3;
/**
+ * A full PC-style keyboard.
+ * <p>
+ * A full keyboard behaves like a PC keyboard. All symbols are accessed directly
+ * by pressing keys on the keyboard without on-screen support or affordances such
+ * as auto-capitalization.
+ * </p><p>
+ * This type of keyboard is generally designed for full two hand typing.
+ * </p>
+ */
+ public static final int FULL = 4;
+
+ /**
+ * A keyboard that is only used to control special functions rather than for typing.
+ * <p>
+ * A special function keyboard consists only of non-printing keys such as
+ * HOME and POWER that are not actually used for typing.
+ * </p>
+ */
+ public static final int SPECIAL_FUNCTION = 5;
+
+ /**
* This private-use character is used to trigger Unicode character
* input by hex digits.
*/
@@ -58,39 +108,73 @@ public class KeyCharacterMap
*/
public static final char PICKER_DIALOG_INPUT = '\uEF01';
- private static Object sLock = new Object();
- private static SparseArray<WeakReference<KeyCharacterMap>> sInstances
- = new SparseArray<WeakReference<KeyCharacterMap>>();
+ /**
+ * Modifier keys may be chorded with character keys.
+ *
+ * @see {#link #getModifierBehavior()} for more details.
+ */
+ public static final int MODIFIER_BEHAVIOR_CHORDED = 0;
+
+ /**
+ * Modifier keys may be chorded with character keys or they may toggle
+ * into latched or locked states when pressed independently.
+ *
+ * @see {#link #getModifierBehavior()} for more details.
+ */
+ public static final int MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED = 1;
+
+ private static SparseArray<KeyCharacterMap> sInstances = new SparseArray<KeyCharacterMap>();
+
+ private final int mDeviceId;
+ private int mPtr;
+
+ private static native int nativeLoad(int id);
+ private static native void nativeDispose(int ptr);
+
+ private static native char nativeGetCharacter(int ptr, int keyCode, int metaState);
+ private static native char nativeGetNumber(int ptr, int keyCode);
+ private static native char nativeGetMatch(int ptr, int keyCode, char[] chars, int metaState);
+ private static native char nativeGetDisplayLabel(int ptr, int keyCode);
+ private static native int nativeGetKeyboardType(int ptr);
+ private static native KeyEvent[] nativeGetEvents(int ptr, int deviceId, char[] chars);
+
+ private KeyCharacterMap(int deviceId, int ptr) {
+ mDeviceId = deviceId;
+ mPtr = ptr;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (mPtr != 0) {
+ nativeDispose(mPtr);
+ mPtr = 0;
+ }
+ }
/**
* Loads the key character maps for the keyboard with the specified device id.
- * @param keyboard The device id of the keyboard.
+ *
+ * @param deviceId The device id of the keyboard.
* @return The associated key character map.
+ * @throws {@link KeyCharacterMapUnavailableException} if the key character map
+ * could not be loaded because it was malformed or the default key character map
+ * is missing from the system.
*/
- public static KeyCharacterMap load(int keyboard)
- {
- synchronized (sLock) {
- KeyCharacterMap result;
- WeakReference<KeyCharacterMap> ref = sInstances.get(keyboard);
- if (ref != null) {
- result = ref.get();
- if (result != null) {
- return result;
- }
+ public static KeyCharacterMap load(int deviceId) {
+ synchronized (sInstances) {
+ KeyCharacterMap map = sInstances.get(deviceId);
+ if (map == null) {
+ int ptr = nativeLoad(deviceId); // might throw
+ map = new KeyCharacterMap(deviceId, ptr);
+ sInstances.put(deviceId, map);
}
- result = new KeyCharacterMap(keyboard);
- sInstances.put(keyboard, new WeakReference<KeyCharacterMap>(result));
- return result;
+ return map;
}
}
- private KeyCharacterMap(int keyboardDevice)
- {
- mKeyboardDevice = keyboardDevice;
- mPointer = ctor_native(keyboardDevice);
- }
-
/**
+ * Gets the Unicode character generated by the specified key and meta
+ * key state combination.
* <p>
* Returns the Unicode character that the specified key would produce
* when the specified meta bits (see {@link MetaKeyKeyListener})
@@ -104,89 +188,116 @@ public class KeyCharacterMap
* actually produce a character -- see {@link #getDeadChar} --
* after masking with {@link #COMBINING_ACCENT_MASK}.
* </p>
+ *
+ * @param keyCode The key code.
+ * @param metaState The meta key modifier state.
+ * @return The associated character or combining accent, or 0 if none.
*/
- public int get(int keyCode, int meta)
- {
- if ((meta & MetaKeyKeyListener.META_CAP_LOCKED) != 0) {
- meta |= KeyEvent.META_SHIFT_ON;
+ public int get(int keyCode, int metaState) {
+ if ((metaState & MetaKeyKeyListener.META_CAP_LOCKED) != 0) {
+ metaState |= KeyEvent.META_CAPS_LOCK_ON;
}
- if ((meta & MetaKeyKeyListener.META_ALT_LOCKED) != 0) {
- meta |= KeyEvent.META_ALT_ON;
+ if ((metaState & MetaKeyKeyListener.META_ALT_LOCKED) != 0) {
+ metaState |= KeyEvent.META_ALT_ON;
}
- // Ignore caps lock on keys where alt and shift have the same effect.
- if ((meta & MetaKeyKeyListener.META_CAP_LOCKED) != 0) {
- if (get_native(mPointer, keyCode, KeyEvent.META_SHIFT_ON) ==
- get_native(mPointer, keyCode, KeyEvent.META_ALT_ON)) {
- meta &= ~KeyEvent.META_SHIFT_ON;
- }
- }
-
- int ret = get_native(mPointer, keyCode, meta);
- int map = COMBINING.get(ret);
-
+ char ch = nativeGetCharacter(mPtr, keyCode, metaState);
+ int map = COMBINING.get(ch);
if (map != 0) {
return map;
} else {
- return ret;
+ return ch;
}
}
/**
- * Gets the number or symbol associated with the key. The character value
- * is returned, not the numeric value. If the key is not a number, but is
- * a symbol, the symbol is retuned.
+ * Gets the number or symbol associated with the key.
+ * <p>
+ * The character value is returned, not the numeric value.
+ * If the key is not a number, but is a symbol, the symbol is retuned.
+ * </p><p>
+ * This method is intended to to support dial pads and other numeric or
+ * symbolic entry on keyboards where certain keys serve dual function
+ * as alphabetic and symbolic keys. This method returns the number
+ * or symbol associated with the key independent of whether the user
+ * has pressed the required modifier.
+ * </p><p>
+ * For example, on one particular keyboard the keys on the top QWERTY row generate
+ * numbers when ALT is pressed such that ALT-Q maps to '1'. So for that keyboard
+ * when {@link #getNumber} is called with {@link KeyEvent#KEYCODE_Q} it returns '1'
+ * so that the user can type numbers without pressing ALT when it makes sense.
+ * </p>
+ *
+ * @param keyCode The key code.
+ * @return The associated numeric or symbolic character, or 0 if none.
*/
- public char getNumber(int keyCode)
- {
- return getNumber_native(mPointer, keyCode);
+ public char getNumber(int keyCode) {
+ return nativeGetNumber(mPtr, keyCode);
}
/**
- * The same as {@link #getMatch(int,char[],int) getMatch(keyCode, chars, 0)}.
+ * Gets the first character in the character array that can be generated
+ * by the specified key code.
+ * <p>
+ * This is a convenience function that returns the same value as
+ * {@link #getMatch(int,char[],int) getMatch(keyCode, chars, 0)}.
+ * </p>
+ *
+ * @param keyCode The keycode.
+ * @param chars The array of matching characters to consider.
+ * @return The matching associated character, or 0 if none.
*/
- public char getMatch(int keyCode, char[] chars)
- {
+ public char getMatch(int keyCode, char[] chars) {
return getMatch(keyCode, chars, 0);
}
/**
- * If one of the chars in the array can be generated by keyCode,
- * return the char; otherwise return '\0'.
- * @param keyCode the key code to look at
- * @param chars the characters to try to find
- * @param modifiers the modifier bits to prefer. If any of these bits
- * are set, if there are multiple choices, that could
- * work, the one for this modifier will be set.
- */
- public char getMatch(int keyCode, char[] chars, int modifiers)
- {
+ * Gets the first character in the character array that can be generated
+ * by the specified key code. If there are multiple choices, prefers
+ * the one that would be generated with the specified meta key modifier state.
+ *
+ * @param keyCode The key code.
+ * @param chars The array of matching characters to consider.
+ * @param metaState The preferred meta key modifier state.
+ * @return The matching associated character, or 0 if none.
+ */
+ public char getMatch(int keyCode, char[] chars, int metaState) {
if (chars == null) {
- // catch it here instead of in native
- throw new NullPointerException();
+ throw new IllegalArgumentException("chars must not be null.");
}
- return getMatch_native(mPointer, keyCode, chars, modifiers);
+ return nativeGetMatch(mPtr, keyCode, chars, metaState);
}
/**
- * Get the primary character for this key. In other words, the label
- * that is physically printed on it.
+ * Gets the primary character for this key.
+ * In other words, the label that is physically printed on it.
+ *
+ * @param keyCode The key code.
+ * @return The display label character, or 0 if none (eg. for non-printing keys).
*/
- public char getDisplayLabel(int keyCode)
- {
- return getDisplayLabel_native(mPointer, keyCode);
+ public char getDisplayLabel(int keyCode) {
+ return nativeGetDisplayLabel(mPtr, keyCode);
}
/**
- * Get the character that is produced by putting accent on the character
- * c.
+ * Get the character that is produced by putting accent on the character c.
* For example, getDeadChar('`', 'e') returns &egrave;.
+ *
+ * @param accent The accent character. eg. '`'
+ * @param c The basic character.
+ * @return The combined character, or 0 if the characters cannot be combined.
*/
- public static int getDeadChar(int accent, int c)
- {
+ public static int getDeadChar(int accent, int c) {
return DEAD.get((accent << 16) | c);
}
+ /**
+ * Describes the character mappings associated with a key.
+ *
+ * @deprecated instead use {@link KeyCharacterMap#getDisplayLabel(int)},
+ * {@link KeyCharacterMap#getNumber(int)} and {@link KeyCharacterMap#get(int, int)}.
+ */
+ @Deprecated
public static class KeyData {
public static final int META_LENGTH = 4;
@@ -212,24 +323,37 @@ public class KeyCharacterMap
*/
public char[] meta = new char[META_LENGTH];
}
-
+
/**
- * Get the characters conversion data for a given keyCode.
+ * Get the character conversion data for a given key code.
*
- * @param keyCode the keyCode to look for
- * @param results a {@link KeyData} that will be filled with the results.
+ * @param keyCode The keyCode to query.
+ * @param results A {@link KeyData} instance that will be filled with the results.
+ * @return True if the key was mapped. If the key was not mapped, results is not modified.
*
- * @return whether the key was mapped or not. If the key was not mapped,
- * results is not modified.
+ * @deprecated instead use {@link KeyCharacterMap#getDisplayLabel(int)},
+ * {@link KeyCharacterMap#getNumber(int)} or {@link KeyCharacterMap#get(int, int)}.
*/
- public boolean getKeyData(int keyCode, KeyData results)
- {
- if (results.meta.length >= KeyData.META_LENGTH) {
- return getKeyData_native(mPointer, keyCode, results);
- } else {
- throw new IndexOutOfBoundsException("results.meta.length must be >= " +
- KeyData.META_LENGTH);
+ @Deprecated
+ public boolean getKeyData(int keyCode, KeyData results) {
+ if (results.meta.length < KeyData.META_LENGTH) {
+ throw new IndexOutOfBoundsException(
+ "results.meta.length must be >= " + KeyData.META_LENGTH);
+ }
+
+ char displayLabel = nativeGetDisplayLabel(mPtr, keyCode);
+ if (displayLabel == 0) {
+ return false;
}
+
+ results.displayLabel = displayLabel;
+ results.number = nativeGetNumber(mPtr, keyCode);
+ results.meta[0] = nativeGetCharacter(mPtr, keyCode, 0);
+ results.meta[1] = nativeGetCharacter(mPtr, keyCode, KeyEvent.META_SHIFT_ON);
+ results.meta[2] = nativeGetCharacter(mPtr, keyCode, KeyEvent.META_ALT_ON);
+ results.meta[3] = nativeGetCharacter(mPtr, keyCode,
+ KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON);
+ return true;
}
/**
@@ -237,102 +361,37 @@ public class KeyCharacterMap
* could plausibly generate the provided sequence of characters. It is
* not guaranteed that the sequence is the only way to generate these
* events or that it is optimal.
- *
- * @return an array of KeyEvent objects, or null if the given char array
+ * <p>
+ * This function is primarily offered for instrumentation and testing purposes.
+ * It may fail to map characters to key codes. In particular, the key character
+ * map for the {@link #BUILT_IN_KEYBOARD built-in keyboard} device id may be empty.
+ * Consider using the key character map associated with the
+ * {@link #VIRTUAL_KEYBOARD virtual keyboard} device id instead.
+ * </p><p>
+ * For robust text entry, do not use this function. Instead construct a
+ * {@link KeyEvent} with action code {@link KeyEvent#ACTION_MULTIPLE} that contains
+ * the desired string using {@link KeyEvent#KeyEvent(long, String, int, int)}.
+ * </p>
+ *
+ * @param chars The sequence of characters to generate.
+ * @return An array of {@link KeyEvent} objects, or null if the given char array
* can not be generated using the current key character map.
*/
- public KeyEvent[] getEvents(char[] chars)
- {
+ public KeyEvent[] getEvents(char[] chars) {
if (chars == null) {
- throw new NullPointerException();
- }
-
- long[] keys = getEvents_native(mPointer, chars);
- if (keys == null) {
- return null;
- }
-
- // how big should the array be
- int len = keys.length*2;
- int N = keys.length;
- for (int i=0; i<N; i++) {
- int mods = (int)(keys[i] >> 32);
- if ((mods & KeyEvent.META_ALT_ON) != 0) {
- len += 2;
- }
- if ((mods & KeyEvent.META_SHIFT_ON) != 0) {
- len += 2;
- }
- if ((mods & KeyEvent.META_SYM_ON) != 0) {
- len += 2;
- }
- }
-
- // create the events
- KeyEvent[] rv = new KeyEvent[len];
- int index = 0;
- long now = SystemClock.uptimeMillis();
- int device = mKeyboardDevice;
- for (int i=0; i<N; i++) {
- int mods = (int)(keys[i] >> 32);
- int meta = 0;
-
- if ((mods & KeyEvent.META_ALT_ON) != 0) {
- meta |= KeyEvent.META_ALT_ON;
- rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_ALT_LEFT, 0, meta, device, 0);
- index++;
- }
- if ((mods & KeyEvent.META_SHIFT_ON) != 0) {
- meta |= KeyEvent.META_SHIFT_ON;
- rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_SHIFT_LEFT, 0, meta, device, 0);
- index++;
- }
- if ((mods & KeyEvent.META_SYM_ON) != 0) {
- meta |= KeyEvent.META_SYM_ON;
- rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_SYM, 0, meta, device, 0);
- index++;
- }
-
- int key = (int)(keys[i]);
- rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
- key, 0, meta, device, 0);
- index++;
- rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
- key, 0, meta, device, 0);
- index++;
-
- if ((mods & KeyEvent.META_ALT_ON) != 0) {
- meta &= ~KeyEvent.META_ALT_ON;
- rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_ALT_LEFT, 0, meta, device, 0);
- index++;
- }
- if ((mods & KeyEvent.META_SHIFT_ON) != 0) {
- meta &= ~KeyEvent.META_SHIFT_ON;
- rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SHIFT_LEFT, 0, meta, device, 0);
- index++;
- }
- if ((mods & KeyEvent.META_SYM_ON) != 0) {
- meta &= ~KeyEvent.META_SYM_ON;
- rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SYM, 0, meta, device, 0);
- index++;
- }
+ throw new IllegalArgumentException("chars must not be null.");
}
-
- return rv;
+ return nativeGetEvents(mPtr, mDeviceId, chars);
}
/**
- * Does this character key produce a glyph?
+ * Returns true if the specified key produces a glyph.
+ *
+ * @param keyCode The key code.
+ * @return True if the key is a printing key.
*/
- public boolean isPrintingKey(int keyCode)
- {
- int type = Character.getType(get(keyCode, 0));
+ public boolean isPrintingKey(int keyCode) {
+ int type = Character.getType(nativeGetDisplayLabel(mPtr, keyCode));
switch (type)
{
@@ -347,22 +406,68 @@ public class KeyCharacterMap
}
}
- protected void finalize() throws Throwable
- {
- dtor_native(mPointer);
+ /**
+ * Gets the keyboard type.
+ * Returns {@link #NUMERIC}, {@link #PREDICTIVE}, {@link #ALPHA} or {@link #FULL}.
+ * <p>
+ * Different keyboard types have different semantics. Refer to the documentation
+ * associated with the keyboard type constants for details.
+ * </p>
+ *
+ * @return The keyboard type.
+ */
+ public int getKeyboardType() {
+ return nativeGetKeyboardType(mPtr);
}
/**
- * Returns {@link #NUMERIC}, {@link #PREDICTIVE} or {@link #ALPHA}.
+ * Gets a constant that describes the behavior of this keyboard's modifier keys
+ * such as {@link KeyEvent#KEYCODE_SHIFT_LEFT}.
+ * <p>
+ * Currently there are two behaviors that may be combined:
+ * </p>
+ * <ul>
+ * <li>Chorded behavior: When the modifier key is pressed together with one or more
+ * character keys, the keyboard inserts the modified keys and
+ * then resets the modifier state when the modifier key is released.</li>
+ * <li>Toggled behavior: When the modifier key is pressed and released on its own
+ * it first toggles into a latched state. When latched, the modifier will apply
+ * to next character key that is pressed and will then reset itself to the initial state.
+ * If the modifier is already latched and the modifier key is pressed and release on
+ * its own again, then it toggles into a locked state. When locked, the modifier will
+ * apply to all subsequent character keys that are pressed until unlocked by pressing
+ * the modifier key on its own one more time to reset it to the initial state.
+ * Toggled behavior is useful for small profile keyboards designed for thumb typing.
+ * </ul>
+ * <p>
+ * This function currently returns {@link #MODIFIER_BEHAVIOR_CHORDED} when the
+ * {@link #getKeyboardType() keyboard type} is {@link #FULL} or {@link #SPECIAL_FUNCTION} and
+ * {@link #MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED} otherwise.
+ * In the future, the function may also take into account global keyboard
+ * accessibility settings, other user preferences, or new device capabilities.
+ * </p>
+ *
+ * @return The modifier behavior for this keyboard.
+ *
+ * @see {@link #MODIFIER_BEHAVIOR_CHORDED}
+ * @see {@link #MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED}
*/
- public int getKeyboardType()
- {
- return getKeyboardType_native(mPointer);
+ public int getModifierBehavior() {
+ switch (getKeyboardType()) {
+ case FULL:
+ case SPECIAL_FUNCTION:
+ return MODIFIER_BEHAVIOR_CHORDED;
+ default:
+ return MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED;
+ }
}
/**
* Queries the framework about whether any physical keys exist on the
- * device that are capable of producing the given key codes.
+ * any keyboard attached to the device that are capable of producing the given key code.
+ *
+ * @param keyCode The key code to query.
+ * @return True if at least one attached keyboard supports the specified key code.
*/
public static boolean deviceHasKey(int keyCode) {
int[] codeArray = new int[1];
@@ -370,7 +475,17 @@ public class KeyCharacterMap
boolean[] ret = deviceHasKeys(codeArray);
return ret[0];
}
-
+
+ /**
+ * Queries the framework about whether any physical keys exist on the
+ * any keyboard attached to the device that are capable of producing the given
+ * array of key codes.
+ *
+ * @param keyCodes The array of key codes to query.
+ * @return A new array of the same size as the key codes array whose elements
+ * are set to true if at least one attached keyboard supports the corresponding key code
+ * at the same index in the key codes array.
+ */
public static boolean[] deviceHasKeys(int[] keyCodes) {
boolean[] ret = new boolean[keyCodes.length];
IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
@@ -382,22 +497,6 @@ public class KeyCharacterMap
return ret;
}
- private int mPointer;
- private int mKeyboardDevice;
-
- private static native int ctor_native(int id);
- private static native void dtor_native(int ptr);
- private static native char get_native(int ptr, int keycode,
- int meta);
- private static native char getNumber_native(int ptr, int keycode);
- private static native char getMatch_native(int ptr, int keycode,
- char[] chars, int modifiers);
- private static native char getDisplayLabel_native(int ptr, int keycode);
- private static native boolean getKeyData_native(int ptr, int keycode,
- KeyData results);
- private static native int getKeyboardType_native(int ptr);
- private static native long[] getEvents_native(int ptr, char[] str);
-
/**
* Maps Unicode combining diacritical to display-form dead key
* (display character shifted left 16 bits).
@@ -412,7 +511,7 @@ public class KeyCharacterMap
/*
* TODO: Change the table format to support full 21-bit-wide
- * accent characters and combined characters if ever necessary.
+ * accent characters and combined characters if ever necessary.
*/
private static final int ACUTE = '\u00B4' << 16;
private static final int GRAVE = '`' << 16;
@@ -550,4 +649,13 @@ public class KeyCharacterMap
DEAD.put(UMLAUT | 'x', '\u1E8D');
DEAD.put(UMLAUT | 'y', '\u00FF');
}
+
+ /**
+ * Thrown by {@link KeyCharacterMap#load} when a key character map could not be loaded.
+ */
+ public static class KeyCharacterMapUnavailableException extends AndroidRuntimeException {
+ public KeyCharacterMapUnavailableException(String msg) {
+ super(msg);
+ }
+ }
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 63c1137..b3277e4 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -18,6 +18,7 @@ package android.view;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.method.MetaKeyKeyListener;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.KeyCharacterMap;
@@ -1170,6 +1171,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
mAction = action;
mKeyCode = code;
mRepeatCount = 0;
+ mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
}
/**
@@ -1192,6 +1194,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
mAction = action;
mKeyCode = code;
mRepeatCount = repeat;
+ mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
}
/**
@@ -1216,6 +1219,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
mKeyCode = code;
mRepeatCount = repeat;
mMetaState = metaState;
+ mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD;
}
/**
@@ -1794,102 +1798,158 @@ public class KeyEvent extends InputEvent implements Parcelable {
* Renamed to {@link #getDeviceId}.
*
* @hide
- * @deprecated
+ * @deprecated use {@link #getDeviceId()} instead.
*/
+ @Deprecated
public final int getKeyboardDevice() {
return mDeviceId;
}
/**
- * Get the primary character for this key. In other words, the label
- * that is physically printed on it.
+ * Gets the {@link KeyCharacterMap} associated with the keyboard device.
+ *
+ * @return The associated key character map.
+ * @throws {@link KeyCharacterMapUnavailableException} if the key character map
+ * could not be loaded because it was malformed or the default key character map
+ * is missing from the system.
+ *
+ * @see {@link KeyCharacterMap#load}
+ */
+ public final KeyCharacterMap getKeyCharacterMap() {
+ return KeyCharacterMap.load(mDeviceId);
+ }
+
+ /**
+ * Gets the primary character for this key.
+ * In other words, the label that is physically printed on it.
+ *
+ * @return The display label character, or 0 if none (eg. for non-printing keys).
*/
public char getDisplayLabel() {
- return KeyCharacterMap.load(mDeviceId).getDisplayLabel(mKeyCode);
+ return getKeyCharacterMap().getDisplayLabel(mKeyCode);
}
/**
+ * Gets the Unicode character generated by the specified key and meta
+ * key state combination.
* <p>
- * Returns the Unicode character that the key would produce.
+ * Returns the Unicode character that the specified key would produce
+ * when the specified meta bits (see {@link MetaKeyKeyListener})
+ * were active.
* </p><p>
* Returns 0 if the key is not one that is used to type Unicode
* characters.
* </p><p>
- * If the return value has bit
- * {@link KeyCharacterMap#COMBINING_ACCENT}
- * set, the key is a "dead key" that should be combined with another to
- * actually produce a character -- see {@link #getDeadChar} --
- * after masking with
- * {@link KeyCharacterMap#COMBINING_ACCENT_MASK}.
+ * If the return value has bit {@link KeyCharacterMap#COMBINING_ACCENT} set, the
+ * key is a "dead key" that should be combined with another to
+ * actually produce a character -- see {@link KeyCharacterMap#getDeadChar} --
+ * after masking with {@link KeyCharacterMap#COMBINING_ACCENT_MASK}.
* </p>
+ *
+ * @return The associated character or combining accent, or 0 if none.
*/
public int getUnicodeChar() {
return getUnicodeChar(mMetaState);
}
/**
+ * Gets the Unicode character generated by the specified key and meta
+ * key state combination.
* <p>
- * Returns the Unicode character that the key would produce.
+ * Returns the Unicode character that the specified key would produce
+ * when the specified meta bits (see {@link MetaKeyKeyListener})
+ * were active.
* </p><p>
* Returns 0 if the key is not one that is used to type Unicode
* characters.
* </p><p>
- * If the return value has bit
- * {@link KeyCharacterMap#COMBINING_ACCENT}
- * set, the key is a "dead key" that should be combined with another to
- * actually produce a character -- see {@link #getDeadChar} -- after masking
- * with {@link KeyCharacterMap#COMBINING_ACCENT_MASK}.
+ * If the return value has bit {@link KeyCharacterMap#COMBINING_ACCENT} set, the
+ * key is a "dead key" that should be combined with another to
+ * actually produce a character -- see {@link KeyCharacterMap#getDeadChar} --
+ * after masking with {@link KeyCharacterMap#COMBINING_ACCENT_MASK}.
* </p>
+ *
+ * @param metaState The meta key modifier state.
+ * @return The associated character or combining accent, or 0 if none.
*/
- public int getUnicodeChar(int meta) {
- return KeyCharacterMap.load(mDeviceId).get(mKeyCode, meta);
+ public int getUnicodeChar(int metaState) {
+ return getKeyCharacterMap().get(mKeyCode, metaState);
}
/**
- * Get the characters conversion data for the key event..
+ * Get the character conversion data for a given key code.
*
- * @param results a {@link KeyData} that will be filled with the results.
+ * @param results A {@link KeyCharacterMap.KeyData} instance that will be
+ * filled with the results.
+ * @return True if the key was mapped. If the key was not mapped, results is not modified.
*
- * @return whether the key was mapped or not. If the key was not mapped,
- * results is not modified.
+ * @deprecated instead use {@link #getDisplayLabel()},
+ * {@link #getNumber()} or {@link #getUnicodeChar(int)}.
*/
+ @Deprecated
public boolean getKeyData(KeyData results) {
- return KeyCharacterMap.load(mDeviceId).getKeyData(mKeyCode, results);
+ return getKeyCharacterMap().getKeyData(mKeyCode, results);
}
/**
- * The same as {@link #getMatch(char[],int) getMatch(chars, 0)}.
+ * Gets the first character in the character array that can be generated
+ * by the specified key code.
+ * <p>
+ * This is a convenience function that returns the same value as
+ * {@link #getMatch(char[],int) getMatch(chars, 0)}.
+ * </p>
+ *
+ * @param chars The array of matching characters to consider.
+ * @return The matching associated character, or 0 if none.
*/
public char getMatch(char[] chars) {
return getMatch(chars, 0);
}
/**
- * If one of the chars in the array can be generated by the keyCode of this
- * key event, return the char; otherwise return '\0'.
- * @param chars the characters to try to find
- * @param modifiers the modifier bits to prefer. If any of these bits
- * are set, if there are multiple choices, that could
- * work, the one for this modifier will be set.
+ * Gets the first character in the character array that can be generated
+ * by the specified key code. If there are multiple choices, prefers
+ * the one that would be generated with the specified meta key modifier state.
+ *
+ * @param chars The array of matching characters to consider.
+ * @param metaState The preferred meta key modifier state.
+ * @return The matching associated character, or 0 if none.
*/
- public char getMatch(char[] chars, int modifiers) {
- return KeyCharacterMap.load(mDeviceId).getMatch(mKeyCode, chars, modifiers);
+ public char getMatch(char[] chars, int metaState) {
+ return getKeyCharacterMap().getMatch(mKeyCode, chars, metaState);
}
/**
- * Gets the number or symbol associated with the key. The character value
- * is returned, not the numeric value. If the key is not a number, but is
- * a symbol, the symbol is retuned.
+ * Gets the number or symbol associated with the key.
+ * <p>
+ * The character value is returned, not the numeric value.
+ * If the key is not a number, but is a symbol, the symbol is retuned.
+ * </p><p>
+ * This method is intended to to support dial pads and other numeric or
+ * symbolic entry on keyboards where certain keys serve dual function
+ * as alphabetic and symbolic keys. This method returns the number
+ * or symbol associated with the key independent of whether the user
+ * has pressed the required modifier.
+ * </p><p>
+ * For example, on one particular keyboard the keys on the top QWERTY row generate
+ * numbers when ALT is pressed such that ALT-Q maps to '1'. So for that keyboard
+ * when {@link #getNumber} is called with {@link KeyEvent#KEYCODE_Q} it returns '1'
+ * so that the user can type numbers without pressing ALT when it makes sense.
+ * </p>
+ *
+ * @return The associated numeric or symbolic character, or 0 if none.
*/
public char getNumber() {
- return KeyCharacterMap.load(mDeviceId).getNumber(mKeyCode);
+ return getKeyCharacterMap().getNumber(mKeyCode);
}
/**
- * Does the key code of this key produce a glyph?
+ * Returns true if this key produces a glyph.
+ *
+ * @return True if the key is a printing key.
*/
public boolean isPrintingKey() {
- return KeyCharacterMap.load(mDeviceId).isPrintingKey(mKeyCode);
+ return getKeyCharacterMap().isPrintingKey(mKeyCode);
}
/**
@@ -2063,7 +2123,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
return "KeyEvent{action=" + actionToString(mAction)
+ " keycode=" + keyCodeToString(mKeyCode)
+ " scancode=" + mScanCode
- + " meta=" + metaStateToString(mMetaState)
+ + " metaState=" + metaStateToString(mMetaState)
+ " flags=0x" + Integer.toHexString(mFlags)
+ " repeat=" + mRepeatCount
+ " device=" + mDeviceId
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index f7c869b..b66fb2c 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -381,11 +381,13 @@ public class BaseInputConnection implements InputConnection {
public boolean performEditorAction(int actionCode) {
long eventTime = SystemClock.uptimeMillis();
sendKeyEvent(new KeyEvent(eventTime, eventTime,
- KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
| KeyEvent.FLAG_EDITOR_ACTION));
sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
- KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+ KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
| KeyEvent.FLAG_EDITOR_ACTION));
return true;
@@ -522,8 +524,7 @@ public class BaseInputConnection implements InputConnection {
// If it's 1 character, we have a chance of being
// able to generate normal key events...
if (mKeyCharacterMap == null) {
- mKeyCharacterMap = KeyCharacterMap.load(
- KeyCharacterMap.BUILT_IN_KEYBOARD);
+ mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
}
char[] chars = new char[1];
content.getChars(0, 1, chars, 0);
@@ -541,7 +542,7 @@ public class BaseInputConnection implements InputConnection {
// Otherwise, revert to the special key event containing
// the actual characters.
KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(),
- content.toString(), KeyCharacterMap.BUILT_IN_KEYBOARD, 0);
+ content.toString(), KeyCharacterMap.VIRTUAL_KEYBOARD, 0);
sendKeyEvent(event);
content.clear();
}
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index f477f8f..1caa707 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -528,8 +528,7 @@ import junit.framework.Assert;
// Otherwise, replace the text being changed (including the last
// character) in the textfield.
TextUtils.getChars(s, start + count - 1, start + count, mCharacter, 0);
- KeyCharacterMap kmap =
- KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+ KeyCharacterMap kmap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
KeyEvent[] events = kmap.getEvents(mCharacter);
boolean cannotUseKeyEvents = null == events;
int charactersFromKeyEvents = cannotUseKeyEvents ? 0 : 1;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f2988bc..383e977 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -55,6 +55,7 @@ import android.util.AttributeSet;
import android.util.EventLog;
import android.util.Log;
import android.view.Gravity;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -7296,7 +7297,7 @@ public class WebView extends AbsoluteLayout
1, (metaState & KeyEvent.META_SHIFT_ON)
| (metaState & KeyEvent.META_ALT_ON)
| (metaState & KeyEvent.META_SYM_ON)
- , 0, 0, 0);
+ , KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0);
mWebViewCore.sendMessage(eventHubAction, event);
}
diff --git a/core/java/android/widget/DialerFilter.java b/core/java/android/widget/DialerFilter.java
index a23887f..20bc114 100644
--- a/core/java/android/widget/DialerFilter.java
+++ b/core/java/android/widget/DialerFilter.java
@@ -81,18 +81,6 @@ public class DialerFilter extends RelativeLayout
// Setup focus & highlight for this view
setFocusable(true);
- // Default the mode based on the keyboard
- KeyCharacterMap kmap
- = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
- mIsQwerty = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC;
- if (mIsQwerty) {
- Log.i("DialerFilter", "This device looks to be QWERTY");
-// setMode(DIGITS_AND_LETTERS);
- } else {
- Log.i("DialerFilter", "This device looks to be 12-KEY");
-// setMode(DIGITS_ONLY);
- }
-
// XXX Force the mode to QWERTY for now, since 12-key isn't supported
mIsQwerty = true;
setMode(DIGITS_AND_LETTERS);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f32ae92..e6503a9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -93,6 +93,7 @@ import android.view.ContextMenu;
import android.view.DragEvent;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -3313,12 +3314,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
long eventTime = SystemClock.uptimeMillis();
h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
new KeyEvent(eventTime, eventTime,
- KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
| KeyEvent.FLAG_EDITOR_ACTION)));
h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
new KeyEvent(SystemClock.uptimeMillis(), eventTime,
- KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+ KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
| KeyEvent.FLAG_EDITOR_ACTION)));
}
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
index 384f7bc..a1a28ac 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
@@ -141,7 +141,7 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
private void sendKeyEventsToTarget(int character) {
Handler handler = mTargetView.getHandler();
- KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.ALPHA).getEvents(
+ KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD).getEvents(
new char[] { (char) character });
if (events != null) {
final int N = events.length;
@@ -158,10 +158,12 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
long eventTime = SystemClock.uptimeMillis();
Handler handler = mTargetView.getHandler();
handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
- new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0,
+ new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)));
handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
- new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0,
+ new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)));
}
diff --git a/core/jni/android_text_KeyCharacterMap.cpp b/core/jni/android_text_KeyCharacterMap.cpp
index 2a23a71..a7e62c1 100644
--- a/core/jni/android_text_KeyCharacterMap.cpp
+++ b/core/jni/android_text_KeyCharacterMap.cpp
@@ -15,159 +15,140 @@
*/
#include <ui/KeyCharacterMap.h>
+#include <ui/Input.h>
-#include <nativehelper/jni.h>
#include <android_runtime/AndroidRuntime.h>
+#include <nativehelper/jni.h>
#include <nativehelper/JNIHelp.h>
+#include "android_view_KeyEvent.h"
+
namespace android {
-static jint
-ctor(JNIEnv *env, jobject clazz, jint id)
-{
- return reinterpret_cast<int>(KeyCharacterMap::load(id));
+static struct {
+ jclass clazz;
+} gKeyEventClassInfo;
+
+static jint nativeLoad(JNIEnv *env, jobject clazz, jint deviceId) {
+ KeyCharacterMap* map;
+ status_t status = KeyCharacterMap::loadByDeviceId(deviceId, &map);
+ if (status) {
+ String8 msg;
+ msg.appendFormat("Could not load key character map for device %d due to error %d. "
+ "Refer to the log for details.", deviceId, status);
+ jniThrowException(env, "android/view/KeyCharacterMap$KeyCharacterMapUnavailableException",
+ msg.string());
+ return 0;
+ }
+ return reinterpret_cast<jint>(map);
}
-static void
-dtor(JNIEnv *env, jobject clazz, jint ptr)
-{
- delete reinterpret_cast<KeyCharacterMap*>(ptr);
+static void nativeDispose(JNIEnv *env, jobject clazz, jint ptr) {
+ KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
+ delete map;
}
-static jchar
-get(JNIEnv *env, jobject clazz, jint ptr, jint keycode, jint meta)
-{
- return reinterpret_cast<KeyCharacterMap*>(ptr)->get(keycode, meta);
+static jchar nativeGetCharacter(JNIEnv *env, jobject clazz, jint ptr,
+ jint keyCode, jint metaState) {
+ KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
+ return map->getCharacter(keyCode, metaState);
}
-static jchar
-getNumber(JNIEnv *env, jobject clazz, jint ptr, jint keycode)
-{
- return reinterpret_cast<KeyCharacterMap*>(ptr)->getNumber(keycode);
+static jchar nativeGetNumber(JNIEnv *env, jobject clazz, jint ptr, jint keyCode) {
+ KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
+ return map->getNumber(keyCode);
}
-static jchar
-getMatch(JNIEnv *env, jobject clazz, jint ptr, jint keycode, jcharArray chars, jint modifiers)
-{
- jchar rv;
- jchar* ch = env->GetCharArrayElements(chars, NULL);
- jsize chsize = env->GetArrayLength(chars);
+static jchar nativeGetMatch(JNIEnv *env, jobject clazz, jint ptr, jint keyCode,
+ jcharArray charsArray, jint metaState) {
+ KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
- rv = reinterpret_cast<KeyCharacterMap*>(ptr)->getMatch(keycode, ch, chsize, modifiers);
+ jsize numChars = env->GetArrayLength(charsArray);
+ jchar* chars = static_cast<jchar*>(env->GetPrimitiveArrayCritical(charsArray, NULL));
+ if (!chars) {
+ return 0;
+ }
- env->ReleaseCharArrayElements(chars, ch, JNI_ABORT);
- return rv;
-}
+ char16_t result = map->getMatch(keyCode, chars, size_t(numChars), metaState);
-static jchar
-getDisplayLabel(JNIEnv *env, jobject clazz, jint ptr, jint keycode)
-{
- return reinterpret_cast<KeyCharacterMap*>(ptr)->getDisplayLabel(keycode);
+ env->ReleasePrimitiveArrayCritical(charsArray, chars, JNI_ABORT);
+ return result;
}
-static jfieldID gKeyDataMetaField;
-static jfieldID gKeyDataNumberField;
-static jfieldID gKeyDataDisplayLabelField;
-
-static jboolean
-getKeyData(JNIEnv *env, jobject clazz, jint ptr, jint keycode, jobject keydata)
-{
- jboolean rv;
-
- unsigned short displayLabel = env->GetCharField(keydata, gKeyDataDisplayLabelField);
- unsigned short number = env->GetCharField(keydata, gKeyDataNumberField);
-
- jcharArray chars = (jcharArray) env->GetObjectField(keydata, gKeyDataMetaField);
- jchar* ch = env->GetCharArrayElements(chars, NULL);
-
- KeyCharacterMap* kmap = reinterpret_cast<KeyCharacterMap*>(ptr);
- rv = kmap->getKeyData(keycode, &displayLabel, &number, ch);
-
- env->SetCharField(keydata, gKeyDataDisplayLabelField, displayLabel);
- env->SetCharField(keydata, gKeyDataNumberField, number);
-
- env->ReleaseCharArrayElements(chars, ch, 0);
- return rv;
+static jchar nativeGetDisplayLabel(JNIEnv *env, jobject clazz, jint ptr, jint keyCode) {
+ KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
+ return map->getDisplayLabel(keyCode);
}
-static jint
-getKeyboardType(JNIEnv *env, jobject clazz, jint ptr)
-{
- return reinterpret_cast<KeyCharacterMap*>(ptr)->getKeyboardType();
+static jint nativeGetKeyboardType(JNIEnv *env, jobject clazz, jint ptr) {
+ KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
+ return map->getKeyboardType();
}
-static jlongArray
-getEvents(JNIEnv *env, jobject clazz, jint ptr, jcharArray jchars)
-{
- KeyCharacterMap* kmap = reinterpret_cast<KeyCharacterMap*>(ptr);
-
- uint16_t* chars = env->GetCharArrayElements(jchars, NULL);
- size_t len = env->GetArrayLength(jchars);
-
- Vector<int32_t> keys;
- Vector<uint32_t> modifiers;
- bool success = kmap->getEvents(chars, len, &keys, &modifiers);
-
- env->ReleaseCharArrayElements(jchars, chars, JNI_ABORT);
-
- if (success) {
- size_t N = keys.size();
+static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jint ptr, jint deviceId,
+ jcharArray charsArray) {
+ KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
- jlongArray rv = env->NewLongArray(N);
- uint64_t* results = (uint64_t*)env->GetLongArrayElements(rv, NULL);
-
- for (size_t i=0; i<N; i++) {
- uint64_t v = modifiers[i];
- v <<= 32;
- v |= keys[i];
- results[i] = v;
- }
-
- env->ReleaseLongArrayElements(rv, (jlong*)results, 0);
- return rv;
- } else {
+ jchar* chars = env->GetCharArrayElements(charsArray, NULL);
+ if (!chars) {
return NULL;
}
+ jsize numChars = env->GetArrayLength(charsArray);
+
+ Vector<KeyEvent> events;
+ jobjectArray result = NULL;
+ if (map->getEvents(deviceId, chars, size_t(numChars), events)) {
+ result = env->NewObjectArray(jsize(events.size()), gKeyEventClassInfo.clazz, NULL);
+ if (result) {
+ for (size_t i = 0; i < events.size(); i++) {
+ jobject keyEventObj = android_view_KeyEvent_fromNative(env, &events.itemAt(i));
+ if (!keyEventObj) break; // threw OOM exception
+ env->SetObjectArrayElement(result, jsize(i), keyEventObj);
+ env->DeleteLocalRef(keyEventObj);
+ }
+ }
+ }
+
+ env->ReleaseCharArrayElements(charsArray, chars, JNI_ABORT);
+ return result;
}
-// ============================================================================
+
/*
* JNI registration.
*/
static JNINativeMethod g_methods[] = {
/* name, signature, funcPtr */
- { "ctor_native", "(I)I", (void*)ctor },
- { "dtor_native", "(I)V", (void*)dtor },
- { "get_native", "(III)C", (void*)get },
- { "getNumber_native", "(II)C", (void*)getNumber },
- { "getMatch_native", "(II[CI)C", (void*)getMatch },
- { "getDisplayLabel_native", "(II)C", (void*)getDisplayLabel },
- { "getKeyData_native", "(IILandroid/view/KeyCharacterMap$KeyData;)Z",
- (void*)getKeyData },
- { "getKeyboardType_native", "(I)I", (void*)getKeyboardType },
- { "getEvents_native", "(I[C)[J", (void*)getEvents }
+ { "nativeLoad", "(I)I",
+ (void*)nativeLoad },
+ { "nativeDispose", "(I)V",
+ (void*)nativeDispose },
+ { "nativeGetCharacter", "(III)C",
+ (void*)nativeGetCharacter },
+ { "nativeGetNumber", "(II)C",
+ (void*)nativeGetNumber },
+ { "nativeGetMatch", "(II[CI)C",
+ (void*)nativeGetMatch },
+ { "nativeGetDisplayLabel", "(II)C",
+ (void*)nativeGetDisplayLabel },
+ { "nativeGetKeyboardType", "(I)I",
+ (void*)nativeGetKeyboardType },
+ { "nativeGetEvents", "(II[C)[Landroid/view/KeyEvent;",
+ (void*)nativeGetEvents },
};
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
int register_android_text_KeyCharacterMap(JNIEnv* env)
{
- jclass clazz;
-
- clazz = env->FindClass("android/view/KeyCharacterMap$KeyData");
- if (clazz == NULL) {
- LOGE("Can't find android/view/KeyCharacterMap$KeyData");
- return -1;
- }
-
- gKeyDataMetaField = env->GetFieldID(clazz, "meta", "[C");
- gKeyDataNumberField = env->GetFieldID(clazz, "number", "C");
- gKeyDataDisplayLabelField = env->GetFieldID(clazz, "displayLabel", "C");
+ FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
return AndroidRuntime::registerNativeMethods(env,
"android/view/KeyCharacterMap", g_methods, NELEM(g_methods));
}
}; // namespace android
-
-
-