diff options
author | Jeff Brown <jeffbrown@google.com> | 2010-11-18 14:20:30 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-11-18 14:20:30 -0800 |
commit | dd4e4603efef2c3df0e26835935f74e1e0e7deb2 (patch) | |
tree | 2ba66f799846ce2707dad9e4c5bb98e50c1291c7 /core | |
parent | 2a26037ddd2d4c3150e3d8ca5d7caf23deae35d5 (diff) | |
parent | 6b53e8daa69cba1a2a5a7c95a01e37ce9c53226c (diff) | |
download | frameworks_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')
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 è. + * + * @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 - - - |