summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2010-11-10 16:03:06 -0800
committerJeff Brown <jeffbrown@google.com>2010-11-18 09:49:03 -0800
commit6b53e8daa69cba1a2a5a7c95a01e37ce9c53226c (patch)
treedb912c6cdf230ef7f2cf406c545b3bbae3f09ea2
parenta914f340ae5b267dc3ab36c1156c795b8fa18f5d (diff)
downloadframeworks_base-6b53e8daa69cba1a2a5a7c95a01e37ce9c53226c.zip
frameworks_base-6b53e8daa69cba1a2a5a7c95a01e37ce9c53226c.tar.gz
frameworks_base-6b53e8daa69cba1a2a5a7c95a01e37ce9c53226c.tar.bz2
Added support for full PC-style keyboards.
BREAKING CHANGE: Redesigned the key character map format to accomodate full keyboards with more comprehensive suite of modifiers. Old key character maps will not work anymore and must be updated. The new format is plain text only and it not compiled to a binary file (so the "kcm" tool will be removed in a subsequent check-in). Added FULL keyboard type to support full PC-style keyboards. Added SPECIAL_FUNCTION keyboard type to support special function keypads that do not have any printable keys suitable for typing and only have keys like HOME and POWER Added a special VIRTUAL_KEYBOARD device id convention that maps to a virtual keyboard with a fixed known layout. This is designed to work around issues injecting input events on devices whose built-in keyboard does not have a useful key character map (ie. when the built-in keyboard is a special function keyboard only.) Modified several places where events were being synthesized to use the virtual keyboard. Removed support for the "qwerty" default layout. The new default layout is "Generic". For the most part "qwerty" was being used as a backstop in case the built-in keyboard did not have a key character map (probably because it was a special function keypad) and the framework needed to be able to inject key events anyways. The latter issue is resolved by using the special VIRTUAL_KEYBOARD device instead of BUILT_IN_KEYBOARD. Added the concept of a key modifier behavior so that MetaKeyKeyListener can distinguish between keyboards that use chorded vs. toggled modifiers. Wrote more robust key layout and key character map parsers to enable support for new keyboard features and user installable key maps. Fixed a bug in InputReader generating key ups when keys are released out of sequence. Updated tons of documentation. Currently QwertyKeyListener is being used for full keyboards with autotext and capitalization disabled. This mostly works but causes some problems with character pickers, etc. These issues will be resolved in subsequent changes. Change-Id: Ica48f6097a551141c215bc0d2c6f7b3fb634d354
-rw-r--r--api/current.xml112
-rwxr-xr-xcmds/input/src/com/android/commands/input/Input.java2
-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
-rw-r--r--data/keyboards/AVRCP.kl (renamed from data/keyboards/common.mk)17
-rw-r--r--data/keyboards/Android.mk27
-rw-r--r--data/keyboards/Generic.kcm557
-rw-r--r--data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm413
-rw-r--r--data/keyboards/Virtual.kcm498
-rw-r--r--data/keyboards/keyboards.mk13
-rw-r--r--include/ui/EventHub.h8
-rw-r--r--include/ui/KeyCharacterMap.h185
-rw-r--r--include/ui/KeyLayoutMap.h65
-rw-r--r--include/ui/Keyboard.h87
-rw-r--r--include/utils/String8.h2
-rw-r--r--include/utils/Tokenizer.h123
-rw-r--r--libs/ui/Android.mk1
-rw-r--r--libs/ui/EventHub.cpp150
-rw-r--r--libs/ui/InputReader.cpp74
-rw-r--r--libs/ui/KeyCharacterMap.cpp950
-rw-r--r--libs/ui/KeyLayoutMap.cpp373
-rw-r--r--libs/ui/KeyLayoutMap.h31
-rw-r--r--libs/ui/Keyboard.cpp244
-rw-r--r--libs/utils/Android.mk1
-rw-r--r--libs/utils/String8.cpp18
-rw-r--r--libs/utils/Tokenizer.cpp150
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java3
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java3
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java4
-rw-r--r--policy/src/com/android/internal/policy/impl/ShortcutManager.java2
46 files changed, 3890 insertions, 1284 deletions
diff --git a/api/current.xml b/api/current.xml
index afe86fb..e2e9b92 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -194886,7 +194886,7 @@
>
<parameter name="keyCode" type="int">
</parameter>
-<parameter name="meta" type="int">
+<parameter name="metaState" type="int">
</parameter>
</method>
<method name="getDeadChar"
@@ -194937,7 +194937,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="keyCode" type="int">
@@ -194985,9 +194985,20 @@
</parameter>
<parameter name="chars" type="char[]">
</parameter>
-<parameter name="modifiers" type="int">
+<parameter name="metaState" type="int">
</parameter>
</method>
+<method name="getModifierBehavior"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getNumber"
return="char"
abstract="false"
@@ -195024,7 +195035,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="keyboard" type="int">
+<parameter name="deviceId" type="int">
</parameter>
</method>
<field name="ALPHA"
@@ -195071,6 +195082,17 @@
visibility="public"
>
</field>
+<field name="FULL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="HEX_INPUT"
type="char"
transient="false"
@@ -195082,6 +195104,28 @@
visibility="public"
>
</field>
+<field name="MODIFIER_BEHAVIOR_CHORDED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="NUMERIC"
type="int"
transient="false"
@@ -195115,13 +195159,54 @@
visibility="public"
>
</field>
+<field name="SPECIAL_FUNCTION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="VIRTUAL_KEYBOARD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="KeyCharacterMap.KeyCharacterMapUnavailableException"
+ extends="android.util.AndroidRuntimeException"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="KeyCharacterMap.KeyCharacterMapUnavailableException"
+ type="android.view.KeyCharacterMap.KeyCharacterMapUnavailableException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="msg" type="java.lang.String">
+</parameter>
+</constructor>
</class>
<class name="KeyCharacterMap.KeyData"
extends="java.lang.Object"
abstract="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="KeyCharacterMap.KeyData"
@@ -195530,6 +195615,17 @@
visibility="public"
>
</method>
+<method name="getKeyCharacterMap"
+ return="android.view.KeyCharacterMap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getKeyCode"
return="int"
abstract="false"
@@ -195548,7 +195644,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="results" type="android.view.KeyCharacterMap.KeyData">
@@ -195579,7 +195675,7 @@
>
<parameter name="chars" type="char[]">
</parameter>
-<parameter name="modifiers" type="int">
+<parameter name="metaState" type="int">
</parameter>
</method>
<method name="getMaxKeyCode"
@@ -195658,7 +195754,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="meta" type="int">
+<parameter name="metaState" type="int">
</parameter>
</method>
<method name="isAltPressed"
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index 3a1accd..df1d0bf 100755
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -91,7 +91,7 @@ public class Input {
char[] chars = buff.toString().toCharArray();
KeyCharacterMap mKeyCharacterMap = KeyCharacterMap.
- load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+ load(KeyCharacterMap.VIRTUAL_KEYBOARD);
KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
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
-
-
-
diff --git a/data/keyboards/common.mk b/data/keyboards/AVRCP.kl
index 7a58b07..4c91ece 100644
--- a/data/keyboards/common.mk
+++ b/data/keyboards/AVRCP.kl
@@ -12,13 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# This is the list of keylayouts and key character maps to build.
-# Used by Android.mk and keyboards.mk.
+# Key layout used for Bluetooth AVRCP support.
-keylayouts := \
- Generic.kl \
- Motorola_Bluetooth_Wireless_Keyboard.kl
-
-keycharmaps := \
- Generic.kcm \
- Motorola_Bluetooth_Wireless_Keyboard.kcm
+key 200 MEDIA_PLAY_PAUSE WAKE
+key 201 MEDIA_PLAY_PAUSE WAKE
+key 166 MEDIA_STOP WAKE
+key 163 MEDIA_NEXT WAKE
+key 165 MEDIA_PREVIOUS WAKE
+key 168 MEDIA_REWIND WAKE
+key 208 MEDIA_FAST_FORWARD WAKE
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
deleted file mode 100644
index 7a0eae0..0000000
--- a/data/keyboards/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# This makefile builds the key character map binary files. (*.kcm.bin)
-
-LOCAL_PATH := $(call my-dir)
-include $(LOCAL_PATH)/common.mk
-
-$(foreach file,$(keycharmaps), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_MODULE_TAGS := optional) \
- $(eval include $(BUILD_KEY_CHAR_MAP)) \
-)
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index f3c52a7..682584c 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -20,81 +20,482 @@
# a new key character map file with the required keyboard configuration.
#
-[type=QWERTY]
-
-# keycode display number base caps fn caps_fn
-
-A 'A' '2' 'a' 'A' 'a' 'A'
-B 'B' '2' 'b' 'B' 'b' 'B'
-C 'C' '2' 'c' 'C' 'c' 'C'
-D 'D' '3' 'd' 'D' 'd' 'D'
-E 'E' '3' 'e' 'E' 'e' 'E'
-F 'F' '3' 'f' 'F' 'f' 'F'
-G 'G' '4' 'g' 'G' 'g' 'G'
-H 'H' '4' 'h' 'H' 'h' 'H'
-I 'I' '4' 'i' 'I' 'i' 'I'
-J 'J' '5' 'j' 'J' 'j' 'J'
-K 'K' '5' 'k' 'K' 'k' 'K'
-L 'L' '5' 'l' 'L' 'l' 'L'
-M 'M' '6' 'm' 'M' 'm' 'M'
-N 'N' '6' 'n' 'N' 'n' 'N'
-O 'O' '6' 'o' 'O' 'o' 'O'
-P 'P' '7' 'p' 'P' 'p' 'P'
-Q 'Q' '7' 'q' 'Q' 'q' 'Q'
-R 'R' '7' 'r' 'R' 'r' 'R'
-S 'S' '7' 's' 'S' 's' 'S'
-T 'T' '8' 't' 'T' 't' 'T'
-U 'U' '8' 'u' 'U' 'u' 'U'
-V 'V' '8' 'v' 'V' 'v' 'V'
-W 'W' '9' 'w' 'W' 'w' 'W'
-X 'X' '9' 'x' 'X' 'x' 'X'
-Y 'Y' '9' 'y' 'Y' 'y' 'Y'
-Z 'Z' '9' 'z' 'Z' 'z' 'Z'
-
-0 '0' '0' '0' ')' '0' ')'
-1 '1' '1' '1' '!' '1' '!'
-2 '2' '2' '2' '@' '2' '@'
-3 '3' '3' '3' '#' '3' '#'
-4 '4' '4' '4' '$' '4' '$'
-5 '5' '5' '5' '%' '5' '%'
-6 '6' '6' '6' '^' '6' '^'
-7 '7' '7' '7' '&' '7' '&'
-8 '8' '8' '8' '*' '8' '*'
-9 '9' '9' '9' '(' '9' '('
-
-SPACE 0x20 0x20 0x20 0x20 0x20 0x20
-ENTER 0xa 0xa 0xa 0xa 0xa 0xa
-TAB 0x9 0x9 0x9 0x9 0x9 0x9
-
-COMMA ',' ',' ',' '<' ',' '<'
-PERIOD '.' '.' '.' '>' '.' '>'
-SLASH '/' '/' '/' '?' '/' '?'
-GRAVE '`' '`' '`' '~' '`' '~'
-MINUS '-' '-' '-' '_' '-' '_'
-EQUALS '=' '=' '=' '+' '=' '+'
-LEFT_BRACKET '[' '[' '[' '{' '[' '{'
-RIGHT_BRACKET ']' ']' ']' '}' ']' '}'
-BACKSLASH '\' '\' '\' '|' '\' '|'
-SEMICOLON ';' ';' ';' ':' ';' ':'
-APOSTROPHE ''' ''' ''' '"' ''' '"'
-
-NUMPAD_0 '0' '0' '0' '0' '0' '0'
-NUMPAD_1 '1' '1' '1' '1' '1' '1'
-NUMPAD_2 '2' '2' '2' '2' '2' '2'
-NUMPAD_3 '3' '3' '3' '3' '3' '3'
-NUMPAD_4 '4' '4' '4' '4' '4' '4'
-NUMPAD_5 '5' '5' '5' '5' '5' '5'
-NUMPAD_6 '6' '6' '6' '6' '6' '6'
-NUMPAD_7 '7' '7' '7' '7' '7' '7'
-NUMPAD_8 '8' '8' '8' '8' '8' '8'
-NUMPAD_9 '9' '9' '9' '9' '9' '9'
-NUMPAD_LEFT_PAREN '(' '(' '(' '(' '(' '('
-NUMPAD_RIGHT_PAREN ')' ')' ')' ')' ')' ')'
-NUMPAD_DIVIDE '/' '/' '/' '/' '/' '/'
-NUMPAD_MULTIPLY '*' '*' '*' '*' '*' '*'
-NUMPAD_SUBTRACT '-' '-' '-' '-' '-' '-'
-NUMPAD_ADD '+' '+' '+' '+' '+' '+'
-NUMPAD_DOT '.' '.' '.' '.' '.' '.'
-NUMPAD_COMMA ',' ',' ',' ',' ',' ','
-NUMPAD_EQUALS '=' '=' '=' '=' '=' '='
-NUMPAD_ENTER 0xa 0xa 0xa 0xa 0xa 0xa
+type FULL
+
+key A {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+ ctrl, alt, meta: none
+}
+
+key B {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+ ctrl, alt, meta: none
+}
+
+key C {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+ ctrl, alt, meta: none
+}
+
+key D {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+ ctrl, alt, meta: none
+}
+
+key E {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+ ctrl, alt, meta: none
+}
+
+key F {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+ ctrl, alt, meta: none
+}
+
+key G {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+ ctrl, alt, meta: none
+}
+
+key H {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+ ctrl, alt, meta: none
+}
+
+key I {
+ label: 'I'
+ base: 'i'
+ shift, capslock: 'I'
+ ctrl, alt, meta: none
+}
+
+key J {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+ ctrl, alt, meta: none
+}
+
+key K {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+ ctrl, alt, meta: none
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+ ctrl, alt, meta: none
+}
+
+key M {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+ ctrl, alt, meta: none
+}
+
+key N {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+ ctrl, alt, meta: none
+}
+
+key O {
+ label: 'O'
+ base: 'o'
+ shift, capslock: 'O'
+ ctrl, alt, meta: none
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+ ctrl, alt, meta: none
+}
+
+key Q {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+ ctrl, alt, meta: none
+}
+
+key R {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+ ctrl, alt, meta: none
+}
+
+key S {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+ ctrl, alt, meta: none
+}
+
+key T {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+ ctrl, alt, meta: none
+}
+
+key U {
+ label: 'U'
+ base: 'u'
+ shift, capslock: 'U'
+ ctrl, alt, meta: none
+}
+
+key V {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+ ctrl, alt, meta: none
+}
+
+key W {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+ ctrl, alt, meta: none
+}
+
+key X {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+ ctrl, alt, meta: none
+}
+
+key Y {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+ ctrl, alt, meta: none
+}
+
+key Z {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+ ctrl, alt, meta: none
+}
+
+key 0 {
+ label, number: '0'
+ base: '0'
+ shift: ')'
+ ctrl, alt, meta: none
+}
+
+key 1 {
+ label, number: '1'
+ base: '1'
+ shift: '!'
+ ctrl, alt, meta: none
+}
+
+key 2 {
+ label, number: '2'
+ base: '2'
+ shift: '@'
+ ctrl, alt, meta: none
+}
+
+key 3 {
+ label, number: '3'
+ base: '3'
+ shift: '#'
+ ctrl, alt, meta: none
+}
+
+key 4 {
+ label, number: '4'
+ base: '4'
+ shift: '$'
+ ctrl, alt, meta: none
+}
+
+key 5 {
+ label, number: '5'
+ base: '5'
+ shift: '%'
+ ctrl, alt, meta: none
+}
+
+key 6 {
+ label, number: '6'
+ base: '6'
+ shift: '^'
+ ctrl, alt, meta: none
+}
+
+key 7 {
+ label, number: '7'
+ base: '7'
+ shift: '&'
+ ctrl, alt, meta: none
+}
+
+key 8 {
+ label, number: '8'
+ base: '8'
+ shift: '*'
+ ctrl, alt, meta: none
+}
+
+key 9 {
+ label, number: '9'
+ base: '9'
+ shift: '('
+ ctrl, alt, meta: none
+}
+
+key SPACE {
+ label: ' '
+ base: ' '
+ ctrl, alt, meta: none
+}
+
+key ENTER {
+ label: '\n'
+ base: '\n'
+ ctrl, alt, meta: none
+}
+
+key TAB {
+ label: '\t'
+ base: '\t'
+ ctrl, alt, meta: none
+}
+
+key COMMA {
+ label, number: ','
+ base: ','
+ shift: '<'
+ ctrl, alt, meta: none
+}
+
+key PERIOD {
+ label, number: '.'
+ base: '.'
+ shift: '>'
+ ctrl, alt, meta: none
+}
+
+key SLASH {
+ label, number: '/'
+ base: '/'
+ shift: '?'
+ ctrl, alt, meta: none
+}
+
+key GRAVE {
+ label, number: '`'
+ base: '`'
+ shift: '~'
+ ctrl, alt, meta: none
+}
+
+key MINUS {
+ label, number: '-'
+ base: '-'
+ shift: '_'
+ ctrl, alt, meta: none
+}
+
+key EQUALS {
+ label, number: '='
+ base: '='
+ shift: '+'
+ ctrl, alt, meta: none
+}
+
+key LEFT_BRACKET {
+ label, number: '['
+ base: '['
+ shift: '{'
+ ctrl, alt, meta: none
+}
+
+key RIGHT_BRACKET {
+ label, number: ']'
+ base: ']'
+ shift: '}'
+ ctrl, alt, meta: none
+}
+
+key BACKSLASH {
+ label, number: '\\'
+ base: '\\'
+ shift: '|'
+ ctrl, alt, meta: none
+}
+
+key SEMICOLON {
+ label, number: ';'
+ base: ';'
+ shift: ':'
+ ctrl, alt, meta: none
+}
+
+key APOSTROPHE {
+ label, number: '\''
+ base: '\''
+ shift: '"'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_0 {
+ label, number: '0'
+ base: fallback INSERT
+ numlock: '0'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_1 {
+ label, number: '1'
+ base: fallback MOVE_END
+ numlock: '1'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_2 {
+ label, number: '2'
+ base: fallback DPAD_DOWN
+ numlock: '2'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_3 {
+ label, number: '3'
+ base: fallback PAGE_DOWN
+ numlock: '3'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_4 {
+ label, number: '4'
+ base: fallback DPAD_LEFT
+ numlock: '4'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_5 {
+ label, number: '5'
+ base: fallback DPAD_CENTER
+ numlock: '5'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_6 {
+ label, number: '6'
+ base: fallback DPAD_RIGHT
+ numlock: '6'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_7 {
+ label, number: '7'
+ base: fallback MOVE_HOME
+ numlock: '7'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_8 {
+ label, number: '8'
+ base: fallback DPAD_UP
+ numlock: '8'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_9 {
+ label, number: '9'
+ base: fallback PAGE_UP
+ numlock: '9'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_LEFT_PAREN {
+ label, number: '('
+ base: '('
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_RIGHT_PAREN {
+ label, number: ')'
+ base: ')'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_DIVIDE {
+ label, number: '/'
+ base: '/'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_MULTIPLY {
+ label, number: '*'
+ base: '*'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_SUBTRACT {
+ label, number: '-'
+ base: '-'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_ADD {
+ label, number: '+'
+ base: '+'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_DOT {
+ label, number: '.'
+ base: fallback FORWARD_DEL
+ numlock: '.'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_COMMA {
+ label, number: ','
+ base: ','
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_EQUALS {
+ label, number: '='
+ base: '='
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_ENTER {
+ label: '\n'
+ base: '\n' fallback ENTER
+ ctrl, alt, meta: none fallback ENTER
+}
diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm
index 0f31683..73a04d0 100644
--- a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm
+++ b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm
@@ -12,60 +12,359 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-[type=QWERTY]
-
-# keycode display number base caps fn caps_fn
-
-A 'A' '2' 'a' 'A' 'a' 'A'
-B 'B' '2' 'b' 'B' 'b' 'B'
-C 'C' '2' 'c' 'C' 'c' 'C'
-D 'D' '3' 'd' 'D' 'd' 'D'
-E 'E' '3' 'e' 'E' 'e' 'E'
-F 'F' '3' 'f' 'F' 'f' 'F'
-G 'G' '4' 'g' 'G' 'g' 'G'
-H 'H' '4' 'h' 'H' 'h' 'H'
-I 'I' '4' 'i' 'I' 'i' 'I'
-J 'J' '5' 'j' 'J' 'j' 'J'
-K 'K' '5' 'k' 'K' 'k' 'K'
-L 'L' '5' 'l' 'L' 'l' 'L'
-M 'M' '6' 'm' 'M' 'm' 'M'
-N 'N' '6' 'n' 'N' 'n' 'N'
-O 'O' '6' 'o' 'O' 'o' 'O'
-P 'P' '7' 'p' 'P' 'p' 'P'
-Q 'Q' '7' 'q' 'Q' 'q' 'Q'
-R 'R' '7' 'r' 'R' 'r' 'R'
-S 'S' '7' 's' 'S' 's' 'S'
-T 'T' '8' 't' 'T' 't' 'T'
-U 'U' '8' 'u' 'U' 'u' 'U'
-V 'V' '8' 'v' 'V' 'v' 'V'
-W 'W' '9' 'w' 'W' 'w' 'W'
-X 'X' '9' 'x' 'X' 'x' 'X'
-Y 'Y' '9' 'y' 'Y' 'y' 'Y'
-Z 'Z' '9' 'z' 'Z' 'z' 'Z'
-
-0 '0' '0' '0' ')' '0' ')'
-1 '1' '1' '1' '!' '1' '!'
-2 '2' '2' '2' '@' '2' '@'
-3 '3' '3' '3' '#' '3' '#'
-4 '4' '4' '4' '$' '4' '$'
-5 '5' '5' '5' '%' '5' '%'
-6 '6' '6' '6' '^' '6' '^'
-7 '7' '7' '7' '&' '7' '&'
-8 '8' '8' '8' '*' '8' '*'
-9 '9' '9' '9' '(' '9' '('
-
-SPACE 0x20 0x20 0x20 0x20 0x20 0x20
-ENTER 0xa 0xa 0xa 0xa 0xa 0xa
-TAB 0x9 0x9 0x9 0x9 0x9 0x9
-
-COMMA ',' ',' ',' '<' ',' '<'
-PERIOD '.' '.' '.' '>' '.' '>'
-SLASH '/' '/' '/' '?' '/' '?'
-GRAVE '`' '`' '`' '~' '`' '~'
-MINUS '-' '-' '-' '_' '-' '_'
-EQUALS '=' '=' '=' '+' '=' '+'
-LEFT_BRACKET '[' '[' '[' '{' '[' '{'
-RIGHT_BRACKET ']' ']' ']' '}' ']' '}'
-BACKSLASH '\' '\' '\' '|' '\' '|'
-SEMICOLON ';' ';' ';' ':' ';' ':'
-APOSTROPHE ''' ''' ''' '"' ''' '"'
+#
+# Generic key character map for full alphabetic US English PC style external keyboards.
+#
+# This file is intentionally very generic and is intended to support a broad rang of keyboards.
+# Do not edit the generic key character map to support a specific keyboard; instead, create
+# a new key character map file with the required keyboard configuration.
+#
+
+type FULL
+
+key A {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+ ctrl, alt, meta: none
+}
+
+key B {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+ ctrl, alt, meta: none
+}
+
+key C {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+ ctrl, alt, meta: none
+}
+
+key D {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+ ctrl, alt, meta: none
+}
+
+key E {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+ ctrl, alt, meta: none
+}
+
+key F {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+ ctrl, alt, meta: none
+}
+
+key G {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+ ctrl, alt, meta: none
+}
+
+key H {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+ ctrl, alt, meta: none
+}
+
+key I {
+ label: 'I'
+ base: 'i'
+ shift, capslock: 'I'
+ ctrl, alt, meta: none
+}
+
+key J {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+ ctrl, alt, meta: none
+}
+
+key K {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+ ctrl, alt, meta: none
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+ ctrl, alt, meta: none
+}
+
+key M {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+ ctrl, alt, meta: none
+}
+
+key N {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+ ctrl, alt, meta: none
+}
+
+key O {
+ label: 'O'
+ base: 'o'
+ shift, capslock: 'O'
+ ctrl, alt, meta: none
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+ ctrl, alt, meta: none
+}
+
+key Q {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+ ctrl, alt, meta: none
+}
+
+key R {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+ ctrl, alt, meta: none
+}
+
+key S {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+ ctrl, alt, meta: none
+}
+
+key T {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+ ctrl, alt, meta: none
+}
+
+key U {
+ label: 'U'
+ base: 'u'
+ shift, capslock: 'U'
+ ctrl, alt, meta: none
+}
+
+key V {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+ ctrl, alt, meta: none
+}
+
+key W {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+ ctrl, alt, meta: none
+}
+
+key X {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+ ctrl, alt, meta: none
+}
+
+key Y {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+ ctrl, alt, meta: none
+}
+
+key Z {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+ ctrl, alt, meta: none
+}
+
+key 0 {
+ label, number: '0'
+ base: '0'
+ shift: ')'
+ ctrl, alt, meta: none
+}
+
+key 1 {
+ label, number: '1'
+ base: '1'
+ shift: '!'
+ ctrl, alt, meta: none
+}
+
+key 2 {
+ label, number: '2'
+ base: '2'
+ shift: '@'
+ ctrl, alt, meta: none
+}
+
+key 3 {
+ label, number: '3'
+ base: '3'
+ shift: '#'
+ ctrl, alt, meta: none
+}
+
+key 4 {
+ label, number: '4'
+ base: '4'
+ shift: '$'
+ ctrl, alt, meta: none
+}
+
+key 5 {
+ label, number: '5'
+ base: '5'
+ shift: '%'
+ ctrl, alt, meta: none
+}
+
+key 6 {
+ label, number: '6'
+ base: '6'
+ shift: '^'
+ ctrl, alt, meta: none
+}
+
+key 7 {
+ label, number: '7'
+ base: '7'
+ shift: '&'
+ ctrl, alt, meta: none
+}
+
+key 8 {
+ label, number: '8'
+ base: '8'
+ shift: '*'
+ ctrl, alt, meta: none
+}
+
+key 9 {
+ label, number: '9'
+ base: '9'
+ shift: '('
+ ctrl, alt, meta: none
+}
+
+key SPACE {
+ label: ' '
+ base: ' '
+ ctrl, alt, meta: none
+}
+
+key ENTER {
+ label: '\n'
+ base: '\n'
+ ctrl, alt, meta: none
+}
+
+key TAB {
+ label: '\t'
+ base: '\t'
+ ctrl, alt, meta: none
+}
+
+key COMMA {
+ label, number: ','
+ base: ','
+ shift: '<'
+ ctrl, alt, meta: none
+}
+
+key PERIOD {
+ label, number: '.'
+ base: '.'
+ shift: '>'
+ ctrl, alt, meta: none
+}
+
+key SLASH {
+ label, number: '/'
+ base: '/'
+ shift: '?'
+ ctrl, alt, meta: none
+}
+
+key GRAVE {
+ label, number: '`'
+ base: '`'
+ shift: '~'
+ ctrl, alt, meta: none
+}
+
+key MINUS {
+ label, number: '-'
+ base: '-'
+ shift: '_'
+ ctrl, alt, meta: none
+}
+
+key EQUALS {
+ label, number: '='
+ base: '='
+ shift: '+'
+ ctrl, alt, meta: none
+}
+
+key LEFT_BRACKET {
+ label, number: '['
+ base: '['
+ shift: '{'
+ ctrl, alt, meta: none
+}
+
+key RIGHT_BRACKET {
+ label, number: ']'
+ base: ']'
+ shift: '}'
+ ctrl, alt, meta: none
+}
+
+key BACKSLASH {
+ label, number: '\\'
+ base: '\\'
+ shift: '|'
+ ctrl, alt, meta: none
+}
+
+key SEMICOLON {
+ label, number: ';'
+ base: ';'
+ shift: ':'
+ ctrl, alt, meta: none
+}
+
+key APOSTROPHE {
+ label, number: '\''
+ base: '\''
+ shift: '"'
+ ctrl, alt, meta: none
+}
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
new file mode 100644
index 0000000..9ada86a
--- /dev/null
+++ b/data/keyboards/Virtual.kcm
@@ -0,0 +1,498 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Key character map for a built-in generic virtual keyboard primarily used
+# for instrumentation and testing purposes.
+#
+
+type FULL
+
+key A {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+ ctrl, alt, meta: none
+}
+
+key B {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+ ctrl, alt, meta: none
+}
+
+key C {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+ ctrl, alt, meta: none
+}
+
+key D {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+ ctrl, alt, meta: none
+}
+
+key E {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+ ctrl, alt, meta: none
+}
+
+key F {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+ ctrl, alt, meta: none
+}
+
+key G {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+ ctrl, alt, meta: none
+}
+
+key H {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+ ctrl, alt, meta: none
+}
+
+key I {
+ label: 'I'
+ base: 'i'
+ shift, capslock: 'I'
+ ctrl, alt, meta: none
+}
+
+key J {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+ ctrl, alt, meta: none
+}
+
+key K {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+ ctrl, alt, meta: none
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+ ctrl, alt, meta: none
+}
+
+key M {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+ ctrl, alt, meta: none
+}
+
+key N {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+ ctrl, alt, meta: none
+}
+
+key O {
+ label: 'O'
+ base: 'o'
+ shift, capslock: 'O'
+ ctrl, alt, meta: none
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+ ctrl, alt, meta: none
+}
+
+key Q {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+ ctrl, alt, meta: none
+}
+
+key R {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+ ctrl, alt, meta: none
+}
+
+key S {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+ ctrl, alt, meta: none
+}
+
+key T {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+ ctrl, alt, meta: none
+}
+
+key U {
+ label: 'U'
+ base: 'u'
+ shift, capslock: 'U'
+ ctrl, alt, meta: none
+}
+
+key V {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+ ctrl, alt, meta: none
+}
+
+key W {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+ ctrl, alt, meta: none
+}
+
+key X {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+ ctrl, alt, meta: none
+}
+
+key Y {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+ ctrl, alt, meta: none
+}
+
+key Z {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+ ctrl, alt, meta: none
+}
+
+key 0 {
+ label, number: '0'
+ base: '0'
+ shift: ')'
+ ctrl, alt, meta: none
+}
+
+key 1 {
+ label, number: '1'
+ base: '1'
+ shift: '!'
+ ctrl, alt, meta: none
+}
+
+key 2 {
+ label, number: '2'
+ base: '2'
+ shift: '@'
+ ctrl, alt, meta: none
+}
+
+key 3 {
+ label, number: '3'
+ base: '3'
+ shift: '#'
+ ctrl, alt, meta: none
+}
+
+key 4 {
+ label, number: '4'
+ base: '4'
+ shift: '$'
+ ctrl, alt, meta: none
+}
+
+key 5 {
+ label, number: '5'
+ base: '5'
+ shift: '%'
+ ctrl, alt, meta: none
+}
+
+key 6 {
+ label, number: '6'
+ base: '6'
+ shift: '^'
+ ctrl, alt, meta: none
+}
+
+key 7 {
+ label, number: '7'
+ base: '7'
+ shift: '&'
+ ctrl, alt, meta: none
+}
+
+key 8 {
+ label, number: '8'
+ base: '8'
+ shift: '*'
+ ctrl, alt, meta: none
+}
+
+key 9 {
+ label, number: '9'
+ base: '9'
+ shift: '('
+ ctrl, alt, meta: none
+}
+
+key SPACE {
+ label: ' '
+ base: ' '
+ ctrl, alt, meta: none
+}
+
+key ENTER {
+ label: '\n'
+ base: '\n'
+ ctrl, alt, meta: none
+}
+
+key TAB {
+ label: '\t'
+ base: '\t'
+ ctrl, alt, meta: none
+}
+
+key COMMA {
+ label, number: ','
+ base: ','
+ shift: '<'
+ ctrl, alt, meta: none
+}
+
+key PERIOD {
+ label, number: '.'
+ base: '.'
+ shift: '>'
+ ctrl, alt, meta: none
+}
+
+key SLASH {
+ label, number: '/'
+ base: '/'
+ shift: '?'
+ ctrl, alt, meta: none
+}
+
+key GRAVE {
+ label, number: '`'
+ base: '`'
+ shift: '~'
+ ctrl, alt, meta: none
+}
+
+key MINUS {
+ label, number: '-'
+ base: '-'
+ shift: '_'
+ ctrl, alt, meta: none
+}
+
+key EQUALS {
+ label, number: '='
+ base: '='
+ shift: '+'
+ ctrl, alt, meta: none
+}
+
+key LEFT_BRACKET {
+ label, number: '['
+ base: '['
+ shift: '{'
+ ctrl, alt, meta: none
+}
+
+key RIGHT_BRACKET {
+ label, number: ']'
+ base: ']'
+ shift: '}'
+ ctrl, alt, meta: none
+}
+
+key BACKSLASH {
+ label, number: '\\'
+ base: '\\'
+ shift: '|'
+ ctrl, alt, meta: none
+}
+
+key SEMICOLON {
+ label, number: ';'
+ base: ';'
+ shift: ':'
+ ctrl, alt, meta: none
+}
+
+key APOSTROPHE {
+ label, number: '\''
+ base: '\''
+ shift: '"'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_0 {
+ label, number: '0'
+ base: fallback INSERT
+ numlock: '0'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_1 {
+ label, number: '1'
+ base: fallback MOVE_END
+ numlock: '1'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_2 {
+ label, number: '2'
+ base: fallback DPAD_DOWN
+ numlock: '2'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_3 {
+ label, number: '3'
+ base: fallback PAGE_DOWN
+ numlock: '3'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_4 {
+ label, number: '4'
+ base: fallback DPAD_LEFT
+ numlock: '4'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_5 {
+ label, number: '5'
+ base: fallback DPAD_CENTER
+ numlock: '5'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_6 {
+ label, number: '6'
+ base: fallback DPAD_RIGHT
+ numlock: '6'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_7 {
+ label, number: '7'
+ base: fallback MOVE_HOME
+ numlock: '7'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_8 {
+ label, number: '8'
+ base: fallback DPAD_UP
+ numlock: '8'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_9 {
+ label, number: '9'
+ base: fallback PAGE_UP
+ numlock: '9'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_LEFT_PAREN {
+ label, number: '('
+ base: '('
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_RIGHT_PAREN {
+ label, number: ')'
+ base: ')'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_DIVIDE {
+ label, number: '/'
+ base: '/'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_MULTIPLY {
+ label, number: '*'
+ base: '*'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_SUBTRACT {
+ label, number: '-'
+ base: '-'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_ADD {
+ label, number: '+'
+ base: '+'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_DOT {
+ label, number: '.'
+ base: fallback FORWARD_DEL
+ numlock: '.'
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_COMMA {
+ label, number: ','
+ base: ','
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_EQUALS {
+ label, number: '='
+ base: '='
+ ctrl, alt, meta: none
+}
+
+key NUMPAD_ENTER {
+ label: '\n'
+ base: '\n' fallback ENTER
+ ctrl, alt, meta: none fallback ENTER
+}
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
index 665c8bf..3a0a553 100644
--- a/data/keyboards/keyboards.mk
+++ b/data/keyboards/keyboards.mk
@@ -14,9 +14,20 @@
# Warning: this is actually a product definition, to be inherited from
-include $(LOCAL_PATH)/common.mk
+keylayouts := \
+ AVRCP.kl \
+ Generic.kl \
+ Motorola_Bluetooth_Wireless_Keyboard.kl
+
+keycharmaps := \
+ Generic.kcm \
+ Virtual.kcm \
+ Motorola_Bluetooth_Wireless_Keyboard.kcm
PRODUCT_COPY_FILES := $(foreach file,$(keylayouts),\
frameworks/base/data/keyboards/$(file):system/usr/keylayout/$(file))
+PRODUCT_COPY_FILES += $(foreach file,$(keycharmaps),\
+ frameworks/base/data/keyboards/$(file):system/usr/keychars/$(file))
+
PRODUCT_PACKAGES := $(keycharmaps)
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index 1431964..f4dc536 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -19,6 +19,7 @@
#define _RUNTIME_EVENT_HUB_H
#include <android/input.h>
+#include <ui/Keyboard.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Log.h>
@@ -246,10 +247,7 @@ private:
uint32_t classes;
uint8_t* keyBitmask;
KeyLayoutMap* layoutMap;
- String8 keyMapName;
- bool defaultKeyMap;
- String8 keyLayoutFilename;
- String8 keyCharacterMapFilename;
+ KeyMapInfo keyMapInfo;
int fd;
device_t* next;
@@ -267,8 +265,6 @@ private:
const int32_t* keyCodes, uint8_t* outFlags) const;
void configureKeyMap(device_t* device);
- bool probeKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap);
- void selectKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap);
void setKeyboardProperties(device_t* device, bool firstKeyboard);
void clearKeyboardProperties(device_t* device, bool firstKeyboard);
diff --git a/include/ui/KeyCharacterMap.h b/include/ui/KeyCharacterMap.h
index bad2cf8..a1ccb37 100644
--- a/include/ui/KeyCharacterMap.h
+++ b/include/ui/KeyCharacterMap.h
@@ -18,55 +18,166 @@
#define _UI_KEY_CHARACTER_MAP_H
#include <stdint.h>
-#include <utils/Vector.h>
-using namespace android;
+#include <ui/Input.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/Tokenizer.h>
+#include <utils/String8.h>
+#include <utils/Unicode.h>
-class KeyCharacterMap
-{
+namespace android {
+
+/**
+ * Describes a mapping from Android key codes to characters.
+ * Also specifies other functions of the keyboard such as the keyboard type
+ * and key modifier semantics.
+ */
+class KeyCharacterMap {
public:
+ enum KeyboardType {
+ KEYBOARD_TYPE_UNKNOWN = 0,
+ KEYBOARD_TYPE_NUMERIC = 1,
+ KEYBOARD_TYPE_PREDICTIVE = 2,
+ KEYBOARD_TYPE_ALPHA = 3,
+ KEYBOARD_TYPE_FULL = 4,
+ KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
+ };
+
~KeyCharacterMap();
- // see the javadoc for android.text.method.KeyCharacterMap for what
- // these do
- unsigned short get(int keycode, int meta);
- unsigned short getNumber(int keycode);
- unsigned short getMatch(int keycode, const unsigned short* chars,
- int charsize, uint32_t modifiers);
- unsigned short getDisplayLabel(int keycode);
- bool getKeyData(int keycode, unsigned short *displayLabel,
- unsigned short *number, unsigned short* results);
- inline unsigned int getKeyboardType() { return m_type; }
- bool getEvents(uint16_t* chars, size_t len,
- Vector<int32_t>* keys, Vector<uint32_t>* modifiers);
-
- static KeyCharacterMap* load(int id);
-
- enum {
- NUMERIC = 1,
- Q14 = 2,
- QWERTY = 3 // or AZERTY or whatever
- };
+ static status_t load(const String8& filename, KeyCharacterMap** outMap);
+ static status_t loadByDeviceId(int32_t deviceId, KeyCharacterMap** outMap);
+
+ /* Gets the keyboard type. */
+ int32_t getKeyboardType() const;
+
+ /* Gets the primary character for this key as in the label physically printed on it.
+ * Returns 0 if none (eg. for non-printing keys). */
+ char16_t getDisplayLabel(int32_t keyCode) const;
+
+ /* Gets the Unicode character for the number or symbol generated by the key
+ * when the keyboard is used as a dialing pad.
+ * Returns 0 if no number or symbol is generated.
+ */
+ char16_t getNumber(int32_t keyCode) const;
-#define META_MASK 3
+ /* Gets the Unicode character generated by the key and meta key modifiers.
+ * Returns 0 if no character is generated.
+ */
+ char16_t getCharacter(int32_t keyCode, int32_t metaState) const;
+
+ /* Gets the first matching Unicode character that can be generated by the key,
+ * preferring the one with the specified meta key modifiers.
+ * Returns 0 if no matching character is generated.
+ */
+ char16_t getMatch(int32_t keyCode, const char16_t* chars,
+ size_t numChars, int32_t metaState) const;
+
+ /* Gets a sequence of key events that could plausibly generate the specified
+ * character sequence. Returns false if some of the characters cannot be generated.
+ */
+ bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
+ Vector<KeyEvent>& outEvents) const;
private:
- struct Key
- {
- int32_t keycode;
- uint16_t display_label;
- uint16_t number;
- uint16_t data[META_MASK + 1];
+ struct Behavior {
+ Behavior();
+
+ /* The next behavior in the list, or NULL if none. */
+ Behavior* next;
+
+ /* The meta key modifiers for this behavior. */
+ int32_t metaState;
+
+ /* The character to insert. */
+ char16_t character;
+
+ /* The fallback keycode if the key is not handled. */
+ int32_t fallbackKeyCode;
+ };
+
+ struct Key {
+ Key();
+ ~Key();
+
+ /* The single character label printed on the key, or 0 if none. */
+ char16_t label;
+
+ /* The number or symbol character generated by the key, or 0 if none. */
+ char16_t number;
+
+ /* The list of key behaviors sorted from most specific to least specific
+ * meta key binding. */
+ Behavior* firstBehavior;
};
+ class Parser {
+ enum State {
+ STATE_TOP = 0,
+ STATE_KEY = 1,
+ };
+
+ enum {
+ PROPERTY_LABEL = 1,
+ PROPERTY_NUMBER = 2,
+ PROPERTY_META = 3,
+ };
+
+ struct Property {
+ inline Property(int32_t property = 0, int32_t metaState = 0) :
+ property(property), metaState(metaState) { }
+
+ int32_t property;
+ int32_t metaState;
+ };
+
+ KeyCharacterMap* mMap;
+ Tokenizer* mTokenizer;
+ State mState;
+ int32_t mKeyCode;
+
+ public:
+ Parser(KeyCharacterMap* map, Tokenizer* tokenizer);
+ ~Parser();
+ status_t parse();
+
+ private:
+ status_t parseType();
+ status_t parseKey();
+ status_t parseKeyProperty();
+ status_t parseModifier(const String8& token, int32_t* outMetaState);
+ status_t parseCharacterLiteral(char16_t* outCharacter);
+ };
+
+ KeyedVector<int32_t, Key*> mKeys;
+ int mType;
+
KeyCharacterMap();
- static KeyCharacterMap* try_file(const char* filename);
- Key* find_key(int keycode);
- bool find_char(uint16_t c, uint32_t* key, uint32_t* mods);
- unsigned int m_type;
- unsigned int m_keyCount;
- Key* m_keys;
+ bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
+
+ static void addKey(Vector<KeyEvent>& outEvents,
+ int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
+ static void addMetaKeys(Vector<KeyEvent>& outEvents,
+ int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
+ int32_t* currentMetaState);
+ static bool addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
+ int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
+ int32_t keyCode, int32_t keyMetaState,
+ int32_t* currentMetaState);
+ static void addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
+ int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
+ int32_t leftKeyCode, int32_t leftKeyMetaState,
+ int32_t rightKeyCode, int32_t rightKeyMetaState,
+ int32_t eitherKeyMetaState,
+ int32_t* currentMetaState);
+ static void addLockedMetaKey(Vector<KeyEvent>& outEvents,
+ int32_t deviceId, int32_t metaState, nsecs_t time,
+ int32_t keyCode, int32_t keyMetaState,
+ int32_t* currentMetaState);
};
+} // namespace android
+
#endif // _UI_KEY_CHARACTER_MAP_H
diff --git a/include/ui/KeyLayoutMap.h b/include/ui/KeyLayoutMap.h
new file mode 100644
index 0000000..f0a6d00
--- /dev/null
+++ b/include/ui/KeyLayoutMap.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_KEY_LAYOUT_MAP_H
+#define _UI_KEY_LAYOUT_MAP_H
+
+#include <stdint.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/Tokenizer.h>
+
+namespace android {
+
+/**
+ * Describes a mapping from keyboard scan codes to Android key codes.
+ */
+class KeyLayoutMap {
+public:
+ ~KeyLayoutMap();
+
+ static status_t load(const String8& filename, KeyLayoutMap** outMap);
+
+ status_t map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const;
+ status_t findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
+
+private:
+ struct Key {
+ int32_t keyCode;
+ uint32_t flags;
+ };
+
+ KeyedVector<int32_t,Key> mKeys;
+
+ KeyLayoutMap();
+
+ class Parser {
+ KeyLayoutMap* mMap;
+ Tokenizer* mTokenizer;
+
+ public:
+ Parser(KeyLayoutMap* map, Tokenizer* tokenizer);
+ ~Parser();
+ status_t parse();
+
+ private:
+ status_t parseKey();
+ };
+};
+
+} // namespace android
+
+#endif // _UI_KEY_LAYOUT_MAP_H
diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h
new file mode 100644
index 0000000..3b477c7
--- /dev/null
+++ b/include/ui/Keyboard.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_KEYBOARD_H
+#define _UI_KEYBOARD_H
+
+#include <ui/Input.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+namespace android {
+
+enum {
+ /* Device id of the built in keyboard. */
+ DEVICE_ID_BUILT_IN_KEYBOARD = 0,
+
+ /* Device id of a generic virtual keyboard with a full layout that can be used
+ * to synthesize key events. */
+ DEVICE_ID_VIRTUAL_KEYBOARD = -1,
+};
+
+struct KeyMapInfo {
+ String8 keyMapName;
+ String8 keyLayoutFile;
+ String8 keyCharacterMapFile;
+ bool isDefaultKeyMap;
+
+ KeyMapInfo() : isDefaultKeyMap(false) {
+ }
+};
+
+/**
+ * Resolves the key map to use for a particular keyboard device.
+ */
+extern status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo);
+
+/**
+ * Sets keyboard system properties.
+ */
+extern void setKeyboardProperties(int32_t deviceId, const String8& deviceName,
+ const KeyMapInfo& keyMapInfo);
+
+/**
+ * Clears keyboard system properties.
+ */
+extern void clearKeyboardProperties(int32_t deviceId);
+
+/**
+ * Gets the key character map filename for a device using inspecting system properties
+ * and then falling back on a default key character map if necessary.
+ * Returns a NAME_NOT_FOUND if none found.
+ */
+extern status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile);
+
+/**
+ * Gets a key code by its short form label, eg. "HOME".
+ * Returns 0 if unknown.
+ */
+extern int32_t getKeyCodeByLabel(const char* label);
+
+/**
+ * Gets a key flag by its short form label, eg. "WAKE".
+ * Returns 0 if unknown.
+ */
+extern uint32_t getKeyFlagByLabel(const char* label);
+
+/**
+ * Updates a meta state field when a key is pressed or released.
+ */
+extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
+
+} // namespace android
+
+#endif // _UI_KEYBOARD_H
diff --git a/include/utils/String8.h b/include/utils/String8.h
index b36f128..6abfb06 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -22,6 +22,7 @@
#include <utils/Unicode.h>
#include <string.h> // for strcmp
+#include <stdarg.h>
// ---------------------------------------------------------------------------
@@ -70,6 +71,7 @@ public:
status_t appendFormat(const char* fmt, ...)
__attribute__((format (printf, 2, 3)));
+ status_t appendFormatV(const char* fmt, va_list args);
// Note that this function takes O(N) time to calculate the value.
// No cache value is stored.
diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h
new file mode 100644
index 0000000..bfe8923
--- /dev/null
+++ b/include/utils/Tokenizer.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UTILS_TOKENIZER_H
+#define _UTILS_TOKENIZER_H
+
+#include <assert.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+namespace android {
+
+/**
+ * A simple tokenizer for loading and parsing ASCII text files line by line.
+ */
+class Tokenizer {
+ Tokenizer(const String8& filename, const char* buffer, size_t length);
+
+public:
+ ~Tokenizer();
+
+ /**
+ * Opens a file and maps it into memory.
+ *
+ * Returns NO_ERROR and a tokenizer for the file, if successful.
+ * Otherwise returns an error and sets outTokenizer to NULL.
+ */
+ static status_t open(const String8& filename, Tokenizer** outTokenizer);
+
+ /**
+ * Returns true if at the end of the file.
+ */
+ inline bool isEof() const { return mCurrent == getEnd(); }
+
+ /**
+ * Returns true if at the end of the line or end of the file.
+ */
+ inline bool isEol() const { return isEof() || *mCurrent == '\n'; }
+
+ /**
+ * Gets the name of the file.
+ */
+ inline String8 getFilename() const { return mFilename; }
+
+ /**
+ * Gets a 1-based line number index for the current position.
+ */
+ inline int32_t getLineNumber() const { return mLineNumber; }
+
+ /**
+ * Formats a location string consisting of the filename and current line number.
+ * Returns a string like "MyFile.txt:33".
+ */
+ String8 getLocation() const;
+
+ /**
+ * Gets the character at the current position.
+ * Returns null at end of file.
+ */
+ inline char peekChar() const { return isEof() ? '\0' : *mCurrent; }
+
+ /**
+ * Gets the remainder of the current line as a string, excluding the newline character.
+ */
+ String8 peekRemainderOfLine() const;
+
+ /**
+ * Gets the character at the current position and advances past it.
+ * Returns null at end of file.
+ */
+ inline char nextChar() { return isEof() ? '\0' : *(mCurrent++); }
+
+ /**
+ * Gets the next token on this line stopping at the specified delimiters
+ * or the end of the line whichever comes first and advances past it.
+ * Also stops at embedded nulls.
+ * Returns the token or an empty string if the current character is a delimiter
+ * or is at the end of the line.
+ */
+ String8 nextToken(const char* delimiters);
+
+ /**
+ * Advances to the next line.
+ * Does nothing if already at the end of the file.
+ */
+ void nextLine();
+
+ /**
+ * Skips over the specified delimiters in the line.
+ * Also skips embedded nulls.
+ */
+ void skipDelimiters(const char* delimiters);
+
+private:
+ Tokenizer(const Tokenizer& other); // not copyable
+
+ String8 mFilename;
+ const char* mBuffer;
+ size_t mLength;
+
+ const char* mCurrent;
+ int32_t mLineNumber;
+
+ inline const char* getEnd() const { return mBuffer + mLength; }
+
+};
+
+} // namespace android
+
+#endif // _UTILS_TOKENIZER_H
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index c4a09d6..61d8abd 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -10,6 +10,7 @@ LOCAL_SRC_FILES:= \
GraphicBufferAllocator.cpp \
GraphicBufferMapper.cpp \
GraphicLog.cpp \
+ Keyboard.cpp \
KeyLayoutMap.cpp \
KeyCharacterMap.cpp \
Input.cpp \
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 9c7e7f4..f468217 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -16,7 +16,6 @@
//#define LOG_NDEBUG 0
#include <ui/EventHub.h>
-#include <ui/KeycodeLabels.h>
#include <hardware_legacy/power.h>
#include <cutils/properties.h>
@@ -33,7 +32,7 @@
#include <errno.h>
#include <assert.h>
-#include "KeyLayoutMap.h"
+#include <ui/KeyLayoutMap.h>
#include <string.h>
#include <stdint.h>
@@ -94,7 +93,7 @@ static inline const char* toString(bool value) {
EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
: id(_id), path(_path), name(name), classes(0)
- , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), defaultKeyMap(false), fd(-1), next(NULL) {
+ , keyBitmask(NULL), layoutMap(NULL), fd(-1), next(NULL) {
}
EventHub::device_t::~device_t() {
@@ -204,8 +203,12 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
}
int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const {
+ if (!device->layoutMap) {
+ return AKEY_STATE_UNKNOWN;
+ }
+
Vector<int32_t> scanCodes;
- device->layoutMap->findScancodes(keyCode, &scanCodes);
+ device->layoutMap->findScanCodes(keyCode, &scanCodes);
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
@@ -273,7 +276,7 @@ bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
scanCodes.clear();
- status_t err = device->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes);
+ status_t err = device->layoutMap->findScanCodes(keyCodes[codeIndex], &scanCodes);
if (! err) {
// check the possible scan codes identified by the layout map against the
// map of codes actually emitted by the driver
@@ -448,14 +451,14 @@ bool EventHub::getEvent(RawEvent* outEvent)
}
outEvent->type = iev.type;
outEvent->scanCode = iev.code;
+ outEvent->flags = 0;
if (iev.type == EV_KEY) {
- status_t err = device->layoutMap->map(iev.code,
- & outEvent->keyCode, & outEvent->flags);
- LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
- iev.code, outEvent->keyCode, outEvent->flags, err);
- if (err != 0) {
- outEvent->keyCode = AKEYCODE_UNKNOWN;
- outEvent->flags = 0;
+ outEvent->keyCode = AKEYCODE_UNKNOWN;
+ if (device->layoutMap) {
+ status_t err = device->layoutMap->map(iev.code,
+ &outEvent->keyCode, &outEvent->flags);
+ LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
+ iev.code, outEvent->keyCode, outEvent->flags, err);
}
} else {
outEvent->keyCode = iev.code;
@@ -800,10 +803,11 @@ int EventHub::openDevice(const char *deviceName) {
device->name = name;
// Configure the keymap for the device.
+
configureKeyMap(device);
// Tell the world about the devname (the descriptive name)
- if (!mHaveFirstKeyboard && !device->defaultKeyMap && strstr(name, "-keypad")) {
+ if (!mHaveFirstKeyboard && !device->keyMapInfo.isDefaultKeyMap && strstr(name, "-keypad")) {
// the built-in keyboard has a well-known device ID of 0,
// this device better not go away.
mHaveFirstKeyboard = true;
@@ -819,11 +823,12 @@ int EventHub::openDevice(const char *deviceName) {
setKeyboardProperties(device, false);
// Load the keylayout.
- if (!device->keyLayoutFilename.isEmpty()) {
- status_t status = device->layoutMap->load(device->keyLayoutFilename);
+ if (!device->keyMapInfo.keyLayoutFile.isEmpty()) {
+ status_t status = KeyLayoutMap::load(device->keyMapInfo.keyLayoutFile,
+ &device->layoutMap);
if (status) {
LOGE("Error %d loading key layout file '%s'.", status,
- device->keyLayoutFilename.string());
+ device->keyMapInfo.keyLayoutFile.string());
}
}
@@ -851,7 +856,8 @@ int EventHub::openDevice(const char *deviceName) {
LOGI("New keyboard: device->id=0x%x devname='%s' keylayout='%s' keycharactermap='%s'\n",
device->id, name,
- device->keyLayoutFilename.string(), device->keyCharacterMapFilename.string());
+ device->keyMapInfo.keyLayoutFile.string(),
+ device->keyMapInfo.keyCharacterMapFile.string());
}
// If the device isn't recognized as something we handle, don't monitor it.
@@ -878,106 +884,17 @@ int EventHub::openDevice(const char *deviceName) {
}
void EventHub::configureKeyMap(device_t* device) {
- // As an initial key map name, try using the device name.
- String8 keyMapName(device->name);
- char* p = keyMapName.lockBuffer(keyMapName.size());
- while (*p) {
- if (*p == ' ') *p = '_';
- p++;
- }
- keyMapName.unlockBuffer();
-
- if (probeKeyMap(device, keyMapName, false)) return;
-
- // TODO Consider allowing the user to configure a specific key map somehow.
-
- // Try the Generic key map.
- // TODO Apply some additional heuristics here to figure out what kind of
- // generic key map to use (US English, etc.).
- keyMapName.setTo("Generic");
- if (probeKeyMap(device, keyMapName, true)) return;
-
- // Fall back on the old style catchall qwerty key map.
- keyMapName.setTo("qwerty");
- if (probeKeyMap(device, keyMapName, true)) return;
-
- // Give up!
- keyMapName.setTo("unknown");
- selectKeyMap(device, keyMapName, true);
- LOGE("Could not determine key map for device '%s'.", device->name.string());
-}
-
-bool EventHub::probeKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap) {
- const char* root = getenv("ANDROID_ROOT");
-
- // TODO Consider also looking somewhere in a writeable partition like /data for a
- // custom keymap supplied by the user for this device.
- bool haveKeyLayout = !device->keyLayoutFilename.isEmpty();
- if (!haveKeyLayout) {
- device->keyLayoutFilename.setTo(root);
- device->keyLayoutFilename.append("/usr/keylayout/");
- device->keyLayoutFilename.append(keyMapName);
- device->keyLayoutFilename.append(".kl");
- if (access(device->keyLayoutFilename.string(), R_OK)) {
- device->keyLayoutFilename.clear();
- } else {
- haveKeyLayout = true;
- }
- }
-
- bool haveKeyCharacterMap = !device->keyCharacterMapFilename.isEmpty();
- if (!haveKeyCharacterMap) {
- device->keyCharacterMapFilename.setTo(root);
- device->keyCharacterMapFilename.append("/usr/keychars/");
- device->keyCharacterMapFilename.append(keyMapName);
- device->keyCharacterMapFilename.append(".kcm.bin");
- if (access(device->keyCharacterMapFilename.string(), R_OK)) {
- device->keyCharacterMapFilename.clear();
- } else {
- haveKeyCharacterMap = true;
- }
- }
-
- if (haveKeyLayout || haveKeyCharacterMap) {
- selectKeyMap(device, keyMapName, defaultKeyMap);
- }
- return haveKeyLayout && haveKeyCharacterMap;
-}
-
-void EventHub::selectKeyMap(device_t* device,
- const String8& keyMapName, bool defaultKeyMap) {
- if (device->keyMapName.isEmpty()) {
- device->keyMapName.setTo(keyMapName);
- device->defaultKeyMap = defaultKeyMap;
- }
+ android::resolveKeyMap(device->name, device->keyMapInfo);
}
void EventHub::setKeyboardProperties(device_t* device, bool firstKeyboard) {
int32_t id = firstKeyboard ? 0 : device->id;
-
- char propName[100];
- sprintf(propName, "hw.keyboards.%u.devname", id);
- property_set(propName, device->name.string());
- sprintf(propName, "hw.keyboards.%u.keymap", id);
- property_set(propName, device->keyMapName.string());
- sprintf(propName, "hw.keyboards.%u.klfile", id);
- property_set(propName, device->keyLayoutFilename.string());
- sprintf(propName, "hw.keyboards.%u.kcmfile", id);
- property_set(propName, device->keyCharacterMapFilename.string());
+ android::setKeyboardProperties(id, device->name, device->keyMapInfo);
}
void EventHub::clearKeyboardProperties(device_t* device, bool firstKeyboard) {
int32_t id = firstKeyboard ? 0 : device->id;
-
- char propName[100];
- sprintf(propName, "hw.keyboards.%u.devname", id);
- property_set(propName, "");
- sprintf(propName, "hw.keyboards.%u.keymap", id);
- property_set(propName, "");
- sprintf(propName, "hw.keyboards.%u.klfile", id);
- property_set(propName, "");
- sprintf(propName, "hw.keyboards.%u.kcmfile", id);
- property_set(propName, "");
+ android::clearKeyboardProperties(id);
}
bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
@@ -987,7 +904,7 @@ bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
}
Vector<int32_t> scanCodes;
- device->layoutMap->findScancodes(keycode, &scanCodes);
+ device->layoutMap->findScanCodes(keycode, &scanCodes);
const size_t N = scanCodes.size();
for (size_t i=0; i<N && i<=KEY_MAX; i++) {
int32_t sc = scanCodes.itemAt(i);
@@ -1139,11 +1056,14 @@ void EventHub::dump(String8& dump) {
}
dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
- dump.appendFormat(INDENT3 "KeyMapName: %s\n", device->keyMapName.string());
- dump.appendFormat(INDENT3 "KeyLayoutFilename: %s\n",
- device->keyLayoutFilename.string());
- dump.appendFormat(INDENT3 "KeyCharacterMapFilename: %s\n",
- device->keyCharacterMapFilename.string());
+ dump.appendFormat(INDENT3 "IsDefaultKeyMap: %s\n",
+ toString(device->keyMapInfo.isDefaultKeyMap));
+ dump.appendFormat(INDENT3 "KeyMapName: %s\n",
+ device->keyMapInfo.keyMapName.string());
+ dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n",
+ device->keyMapInfo.keyLayoutFile.string());
+ dump.appendFormat(INDENT3 "KeyCharacterMapFile: %s\n",
+ device->keyMapInfo.keyCharacterMapFile.string());
}
}
} // release lock
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index b91e93a..daff2d0 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -24,6 +24,7 @@
#include <cutils/log.h>
#include <ui/InputReader.h>
+#include <ui/Keyboard.h>
#include <stddef.h>
#include <stdlib.h>
@@ -70,75 +71,6 @@ static inline const char* toString(bool value) {
return value ? "true" : "false";
}
-int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
- int32_t newMetaState;
- if (down) {
- newMetaState = oldMetaState | mask;
- } else {
- newMetaState = oldMetaState &
- ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
- }
-
- if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
- newMetaState |= AMETA_ALT_ON;
- }
-
- if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
- newMetaState |= AMETA_SHIFT_ON;
- }
-
- if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
- newMetaState |= AMETA_CTRL_ON;
- }
-
- if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
- newMetaState |= AMETA_META_ON;
- }
- return newMetaState;
-}
-
-int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
- if (down) {
- return oldMetaState;
- } else {
- return oldMetaState ^ mask;
- }
-}
-
-int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
- int32_t mask;
- switch (keyCode) {
- case AKEYCODE_ALT_LEFT:
- return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
- case AKEYCODE_ALT_RIGHT:
- return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
- case AKEYCODE_SHIFT_LEFT:
- return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
- case AKEYCODE_SHIFT_RIGHT:
- return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
- case AKEYCODE_SYM:
- return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
- case AKEYCODE_FUNCTION:
- return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
- case AKEYCODE_CTRL_LEFT:
- return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
- case AKEYCODE_CTRL_RIGHT:
- return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
- case AKEYCODE_META_LEFT:
- return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
- case AKEYCODE_META_RIGHT:
- return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
- case AKEYCODE_CAPS_LOCK:
- return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
- case AKEYCODE_NUM_LOCK:
- return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
- case AKEYCODE_SCROLL_LOCK:
- return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
- default:
- return oldMetaState;
- }
-}
-
static const int32_t keyCodeRotationMap[][4] = {
// key codes enumerated counter-clockwise with the original (unrotated) key first
// no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
@@ -977,7 +909,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
ssize_t keyDownIndex = findKeyDownLocked(scanCode);
if (keyDownIndex >= 0) {
// key repeat, be sure to use same keycode as before in case of rotation
- keyCode = mLocked.keyDowns.top().keyCode;
+ keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
} else {
// key down
mLocked.keyDowns.push();
@@ -992,7 +924,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
ssize_t keyDownIndex = findKeyDownLocked(scanCode);
if (keyDownIndex >= 0) {
// key up, be sure to use same keycode as before in case of rotation
- keyCode = mLocked.keyDowns.top().keyCode;
+ keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
mLocked.keyDowns.removeAt(size_t(keyDownIndex));
} else {
// key was not actually down
diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp
index 870a45c..890cc3f 100644
--- a/libs/ui/KeyCharacterMap.cpp
+++ b/libs/ui/KeyCharacterMap.cpp
@@ -1,275 +1,807 @@
-#define LOG_TAG "KeyCharacterMap"
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
-#include <ui/KeyCharacterMap.h>
-#include <cutils/properties.h>
+#define LOG_TAG "KeyCharacterMap"
-#include <utils/Log.h>
-#include <sys/types.h>
-#include <unistd.h>
#include <stdlib.h>
-#include <fcntl.h>
-#include <limits.h>
#include <string.h>
+#include <android/keycodes.h>
+#include <ui/Keyboard.h>
+#include <ui/KeyCharacterMap.h>
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/Tokenizer.h>
+#include <utils/Timers.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+// Enables debug output for mapping.
+#define DEBUG_MAPPING 0
+
+
+namespace android {
-struct Header
-{
- char magic[8];
- unsigned int endian;
- unsigned int version;
- unsigned int keycount;
- unsigned char kbdtype;
- char padding[11];
+static const char* WHITESPACE = " \t\r";
+static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:";
+
+struct Modifier {
+ const char* label;
+ int32_t metaState;
+};
+static const Modifier modifiers[] = {
+ { "shift", AMETA_SHIFT_ON },
+ { "lshift", AMETA_SHIFT_LEFT_ON },
+ { "rshift", AMETA_SHIFT_RIGHT_ON },
+ { "alt", AMETA_ALT_ON },
+ { "lalt", AMETA_ALT_LEFT_ON },
+ { "ralt", AMETA_ALT_RIGHT_ON },
+ { "ctrl", AMETA_CTRL_ON },
+ { "lctrl", AMETA_CTRL_LEFT_ON },
+ { "rctrl", AMETA_CTRL_RIGHT_ON },
+ { "meta", AMETA_META_ON },
+ { "lmeta", AMETA_META_LEFT_ON },
+ { "rmeta", AMETA_META_RIGHT_ON },
+ { "sym", AMETA_SYM_ON },
+ { "fn", AMETA_FUNCTION_ON },
+ { "capslock", AMETA_CAPS_LOCK_ON },
+ { "numlock", AMETA_NUM_LOCK_ON },
+ { "scrolllock", AMETA_SCROLL_LOCK_ON },
};
-KeyCharacterMap::KeyCharacterMap()
-{
+#if DEBUG_MAPPING
+static String8 toString(const char16_t* chars, size_t numChars) {
+ String8 result;
+ for (size_t i = 0; i < numChars; i++) {
+ result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]);
+ }
+ return result;
}
+#endif
+
+
+// --- KeyCharacterMap ---
-KeyCharacterMap::~KeyCharacterMap()
-{
- free(m_keys);
+KeyCharacterMap::KeyCharacterMap() :
+ mType(KEYBOARD_TYPE_UNKNOWN) {
}
-unsigned short
-KeyCharacterMap::get(int keycode, int meta)
-{
- Key* k = find_key(keycode);
- if (k != NULL) {
- return k->data[meta & META_MASK];
+KeyCharacterMap::~KeyCharacterMap() {
+ for (size_t i = 0; i < mKeys.size(); i++) {
+ Key* key = mKeys.editValueAt(i);
+ delete key;
}
- return 0;
}
-unsigned short
-KeyCharacterMap::getNumber(int keycode)
-{
- Key* k = find_key(keycode);
- if (k != NULL) {
- return k->number;
+status_t KeyCharacterMap::load(const String8& filename, KeyCharacterMap** outMap) {
+ *outMap = NULL;
+
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::open(filename, &tokenizer);
+ if (status) {
+ LOGE("Error %d opening key character map file %s.", status, filename.string());
+ } else {
+ KeyCharacterMap* map = new KeyCharacterMap();
+ if (!map) {
+ LOGE("Error allocating key character map.");
+ status = NO_MEMORY;
+ } else {
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map, tokenizer);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ LOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ elapsedTime / 1000000.0);
+#endif
+ if (status) {
+ delete map;
+ } else {
+ *outMap = map;
+ }
+ }
+ delete tokenizer;
}
- return 0;
+ return status;
}
-unsigned short
-KeyCharacterMap::getMatch(int keycode, const unsigned short* chars,
- int charsize, uint32_t modifiers)
-{
- Key* k = find_key(keycode);
- modifiers &= 3; // ignore the SYM key because we don't have keymap entries for it
- if (k != NULL) {
- const uint16_t* data = k->data;
- for (int j=0; j<charsize; j++) {
- uint16_t c = chars[j];
- for (int i=0; i<(META_MASK + 1); i++) {
- if ((modifiers == 0) || ((modifiers & i) != 0)) {
- if (c == data[i]) {
- return c;
+status_t KeyCharacterMap::loadByDeviceId(int32_t deviceId, KeyCharacterMap** outMap) {
+ *outMap = NULL;
+
+ String8 filename;
+ status_t result = getKeyCharacterMapFile(deviceId, filename);
+ if (!result) {
+ result = load(filename, outMap);
+ }
+ return result;
+}
+
+int32_t KeyCharacterMap::getKeyboardType() const {
+ return mType;
+}
+
+char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
+ char16_t result = 0;
+ ssize_t index = mKeys.indexOfKey(keyCode);
+ if (index >= 0) {
+ const Key* key = mKeys.valueAt(index);
+ result = key->label;
+ }
+#if DEBUG_MAPPING
+ LOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result);
+#endif
+ return result;
+}
+
+char16_t KeyCharacterMap::getNumber(int32_t keyCode) const {
+ char16_t result = 0;
+ ssize_t index = mKeys.indexOfKey(keyCode);
+ if (index >= 0) {
+ const Key* key = mKeys.valueAt(index);
+ result = key->number;
+ }
+#if DEBUG_MAPPING
+ LOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result);
+#endif
+ return result;
+}
+
+char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const {
+ char16_t result = 0;
+ ssize_t index = mKeys.indexOfKey(keyCode);
+ if (index >= 0) {
+ const Key* key = mKeys.valueAt(index);
+ for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
+ if ((behavior->metaState & metaState) == behavior->metaState) {
+ result = behavior->character;
+ break;
+ }
+ }
+ }
+#if DEBUG_MAPPING
+ LOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result);
+#endif
+ return result;
+}
+
+char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars,
+ int32_t metaState) const {
+ char16_t result = 0;
+ ssize_t index = mKeys.indexOfKey(keyCode);
+ if (index >= 0) {
+ const Key* key = mKeys.valueAt(index);
+
+ // Try to find the most general behavior that maps to this character.
+ // For example, the base key behavior will usually be last in the list.
+ // However, if we find a perfect meta state match for one behavior then use that one.
+ for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
+ if (behavior->character) {
+ for (size_t i = 0; i < numChars; i++) {
+ if (behavior->character == chars[i]) {
+ result = behavior->character;
+ if ((behavior->metaState & metaState) == behavior->metaState) {
+ goto ExactMatch;
+ }
+ break;
}
}
}
}
+ ExactMatch: ;
}
- return 0;
+#if DEBUG_MAPPING
+ LOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.",
+ keyCode, toString(chars, numChars).string(), metaState, result);
+#endif
+ return result;
}
-unsigned short
-KeyCharacterMap::getDisplayLabel(int keycode)
-{
- Key* k = find_key(keycode);
- if (k != NULL) {
- return k->display_label;
+bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
+ Vector<KeyEvent>& outEvents) const {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ for (size_t i = 0; i < numChars; i++) {
+ int32_t keyCode, metaState;
+ char16_t ch = chars[i];
+ if (!findKey(ch, &keyCode, &metaState)) {
+#if DEBUG_MAPPING
+ LOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.",
+ deviceId, toString(chars, numChars).string(), ch);
+#endif
+ return false;
+ }
+
+ int32_t currentMetaState = 0;
+ addMetaKeys(outEvents, deviceId, metaState, true, now, &currentMetaState);
+ addKey(outEvents, deviceId, keyCode, currentMetaState, true, now);
+ addKey(outEvents, deviceId, keyCode, currentMetaState, false, now);
+ addMetaKeys(outEvents, deviceId, metaState, false, now, &currentMetaState);
+ }
+#if DEBUG_MAPPING
+ LOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.",
+ deviceId, toString(chars, numChars).string(), outEvents.size());
+ for (size_t i = 0; i < outEvents.size(); i++) {
+ LOGD(" Key: keyCode=%d, metaState=0x%08x, %s.",
+ outEvents[i].getKeyCode(), outEvents[i].getMetaState(),
+ outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up");
}
- return 0;
+#endif
+ return true;
}
-bool
-KeyCharacterMap::getKeyData(int keycode, unsigned short *displayLabel,
- unsigned short *number, unsigned short* results)
-{
- Key* k = find_key(keycode);
- if (k != NULL) {
- memcpy(results, k->data, sizeof(short)*(META_MASK + 1));
- *number = k->number;
- *displayLabel = k->display_label;
- return true;
- } else {
+bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const {
+ if (!ch) {
return false;
}
-}
-bool
-KeyCharacterMap::find_char(uint16_t c, uint32_t* key, uint32_t* mods)
-{
- uint32_t N = m_keyCount;
- for (int j=0; j<(META_MASK + 1); j++) {
- Key const* keys = m_keys;
- for (uint32_t i=0; i<N; i++) {
- if (keys->data[j] == c) {
- *key = keys->keycode;
- *mods = j;
- return true;
+ for (size_t i = 0; i < mKeys.size(); i++) {
+ const Key* key = mKeys.valueAt(i);
+
+ // Try to find the most general behavior that maps to this character.
+ // For example, the base key behavior will usually be last in the list.
+ const Behavior* found = NULL;
+ for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
+ if (behavior->character == ch) {
+ found = behavior;
}
- keys++;
+ }
+ if (found) {
+ *outKeyCode = mKeys.keyAt(i);
+ *outMetaState = found->metaState;
+ return true;
}
}
return false;
}
-bool
-KeyCharacterMap::getEvents(uint16_t* chars, size_t len,
- Vector<int32_t>* keys, Vector<uint32_t>* modifiers)
-{
- for (size_t i=0; i<len; i++) {
- uint32_t k, mods;
- if (find_char(chars[i], &k, &mods)) {
- keys->add(k);
- modifiers->add(mods);
- } else {
- return false;
- }
+void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents,
+ int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
+ outEvents.push();
+ KeyEvent& event = outEvents.editTop();
+ event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD,
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+ 0, keyCode, 0, metaState, 0, time, time);
+}
+
+void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
+ int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
+ int32_t* currentMetaState) {
+ // Add and remove meta keys symmetrically.
+ if (down) {
+ addLockedMetaKey(outEvents, deviceId, metaState, time,
+ AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
+ addLockedMetaKey(outEvents, deviceId, metaState, time,
+ AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
+ addLockedMetaKey(outEvents, deviceId, metaState, time,
+ AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
+
+ addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
+ AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
+ AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
+ AMETA_SHIFT_ON, currentMetaState);
+ addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
+ AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
+ AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
+ AMETA_ALT_ON, currentMetaState);
+ addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
+ AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
+ AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
+ AMETA_CTRL_ON, currentMetaState);
+ addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
+ AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
+ AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
+ AMETA_META_ON, currentMetaState);
+
+ addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
+ AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
+ addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time,
+ AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
+ } else {
+ addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
+ AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState);
+ addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
+ AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState);
+
+ addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
+ AKEYCODE_META_LEFT, AMETA_META_LEFT_ON,
+ AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON,
+ AMETA_META_ON, currentMetaState);
+ addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
+ AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON,
+ AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON,
+ AMETA_CTRL_ON, currentMetaState);
+ addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
+ AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON,
+ AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON,
+ AMETA_ALT_ON, currentMetaState);
+ addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time,
+ AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON,
+ AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON,
+ AMETA_SHIFT_ON, currentMetaState);
+
+ addLockedMetaKey(outEvents, deviceId, metaState, time,
+ AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState);
+ addLockedMetaKey(outEvents, deviceId, metaState, time,
+ AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState);
+ addLockedMetaKey(outEvents, deviceId, metaState, time,
+ AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState);
}
- return true;
}
-KeyCharacterMap::Key*
-KeyCharacterMap::find_key(int keycode)
-{
- Key* keys = m_keys;
- int low = 0;
- int high = m_keyCount - 1;
- int mid;
- int n;
- while (low <= high) {
- mid = (low + high) / 2;
- n = keys[mid].keycode;
- if (keycode < n) {
- high = mid - 1;
- } else if (keycode > n) {
- low = mid + 1;
- } else {
- return keys + mid;
- }
+bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
+ int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
+ int32_t keyCode, int32_t keyMetaState,
+ int32_t* currentMetaState) {
+ if ((metaState & keyMetaState) == keyMetaState) {
+ *currentMetaState = updateMetaState(keyCode, down, *currentMetaState);
+ addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time);
+ return true;
}
- return NULL;
+ return false;
}
-KeyCharacterMap*
-KeyCharacterMap::load(int id)
-{
- KeyCharacterMap* map;
- char path[PATH_MAX];
- char propName[100];
- char dev[PROPERTY_VALUE_MAX];
- char fn[PROPERTY_VALUE_MAX];
- int err;
-
- // Check whether the EventHub has set a key character map filename for us already.
- sprintf(propName, "hw.keyboards.%u.kcmfile", id);
- err = property_get(propName, fn, "");
- if (err > 0) {
- map = try_file(fn);
- if (map) {
- return map;
- }
- LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, fn);
- }
-
- // Try using the device name.
- const char* root = getenv("ANDROID_ROOT");
-
- sprintf(propName, "hw.keyboards.%u.devname", id);
- err = property_get(propName, dev, "");
- if (err > 0) {
- // replace all the spaces with underscores
- strcpy(fn, dev);
- for (char *p = strchr(fn, ' '); p && *p; p = strchr(p + 1, ' '))
- *p = '_';
- snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, fn);
- map = try_file(path);
- if (map) {
- return map;
+void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents,
+ int32_t deviceId, int32_t metaState, bool down, nsecs_t time,
+ int32_t leftKeyCode, int32_t leftKeyMetaState,
+ int32_t rightKeyCode, int32_t rightKeyMetaState,
+ int32_t eitherKeyMetaState,
+ int32_t* currentMetaState) {
+ bool specific = false;
+ specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
+ leftKeyCode, leftKeyMetaState, currentMetaState);
+ specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
+ rightKeyCode, rightKeyMetaState, currentMetaState);
+
+ if (!specific) {
+ addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time,
+ leftKeyCode, eitherKeyMetaState, currentMetaState);
+ }
+}
+
+void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents,
+ int32_t deviceId, int32_t metaState, nsecs_t time,
+ int32_t keyCode, int32_t keyMetaState,
+ int32_t* currentMetaState) {
+ if ((metaState & keyMetaState) == keyMetaState) {
+ *currentMetaState = updateMetaState(keyCode, true, *currentMetaState);
+ addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time);
+ *currentMetaState = updateMetaState(keyCode, false, *currentMetaState);
+ addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time);
+ }
+}
+
+
+// --- KeyCharacterMap::Key ---
+
+KeyCharacterMap::Key::Key() :
+ label(0), number(0), firstBehavior(NULL) {
+}
+
+KeyCharacterMap::Key::~Key() {
+ Behavior* behavior = firstBehavior;
+ while (behavior) {
+ Behavior* next = behavior->next;
+ delete behavior;
+ behavior = next;
+ }
+}
+
+
+// --- KeyCharacterMap::Behavior ---
+
+KeyCharacterMap::Behavior::Behavior() :
+ next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
+}
+
+
+// --- KeyCharacterMap::Parser ---
+
+KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) :
+ mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) {
+}
+
+KeyCharacterMap::Parser::~Parser() {
+}
+
+status_t KeyCharacterMap::Parser::parse() {
+ while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+ LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+#endif
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+ switch (mState) {
+ case STATE_TOP: {
+ String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
+ if (keywordToken == "type") {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ status_t status = parseType();
+ if (status) return status;
+ } else if (keywordToken == "key") {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ status_t status = parseKey();
+ if (status) return status;
+ } else {
+ LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
+ keywordToken.string());
+ return BAD_VALUE;
+ }
+ break;
+ }
+
+ case STATE_KEY: {
+ status_t status = parseKeyProperty();
+ if (status) return status;
+ break;
+ }
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ if (!mTokenizer->isEol()) {
+ LOGE("%s: Expected end of line, got '%s'.",
+ mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+ return BAD_VALUE;
+ }
}
- LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev);
- } else {
- LOGW("No keyboard for id %d", id);
+
+ mTokenizer->nextLine();
}
- snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root);
- map = try_file(path);
- if (map) {
- LOGW("Using default keymap: %s", path);
- return map;
+ if (mState != STATE_TOP) {
+ LOGE("%s: Unterminated key description at end of file.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
}
- LOGE("Can't find any keycharmaps (also tried %s)", path);
- return NULL;
+ if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
+ LOGE("%s: Missing required keyboard 'type' declaration.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
}
-KeyCharacterMap*
-KeyCharacterMap::try_file(const char* filename)
-{
- KeyCharacterMap* rv = NULL;
- Key* keys;
- int fd;
- off_t filesize;
- Header header;
- int err;
-
- fd = open(filename, O_RDONLY);
- if (fd == -1) {
- LOGW("Can't open keycharmap file");
- return NULL;
+status_t KeyCharacterMap::Parser::parseType() {
+ if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
+ LOGE("%s: Duplicate keyboard 'type' declaration.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
}
- filesize = lseek(fd, 0, SEEK_END);
- lseek(fd, 0, SEEK_SET);
+ KeyboardType type;
+ String8 typeToken = mTokenizer->nextToken(WHITESPACE);
+ if (typeToken == "NUMERIC") {
+ type = KEYBOARD_TYPE_NUMERIC;
+ } else if (typeToken == "PREDICTIVE") {
+ type = KEYBOARD_TYPE_PREDICTIVE;
+ } else if (typeToken == "ALPHA") {
+ type = KEYBOARD_TYPE_ALPHA;
+ } else if (typeToken == "FULL") {
+ type = KEYBOARD_TYPE_FULL;
+ } else if (typeToken == "SPECIAL_FUNCTION") {
+ type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
+ } else {
+ LOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
+ typeToken.string());
+ return BAD_VALUE;
+ }
+
+#if DEBUG_PARSER
+ LOGD("Parsed type: type=%d.", type);
+#endif
+ mMap->mType = type;
+ return NO_ERROR;
+}
- // validate the header
- if (filesize <= (off_t)sizeof(header)) {
- LOGW("Bad keycharmap - filesize=%d\n", (int)filesize);
- goto cleanup1;
+status_t KeyCharacterMap::Parser::parseKey() {
+ String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
+ int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+ if (!keyCode) {
+ LOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
+ keyCodeToken.string());
+ return BAD_VALUE;
+ }
+ if (mMap->mKeys.indexOfKey(keyCode) >= 0) {
+ LOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
+ keyCodeToken.string());
+ return BAD_VALUE;
}
- err = read(fd, &header, sizeof(header));
- if (err == -1) {
- LOGW("Error reading keycharmap file");
- goto cleanup1;
+ mTokenizer->skipDelimiters(WHITESPACE);
+ String8 openBraceToken = mTokenizer->nextToken(WHITESPACE);
+ if (openBraceToken != "{") {
+ LOGE("%s: Expected '{' after key code label, got '%s'.",
+ mTokenizer->getLocation().string(), openBraceToken.string());
+ return BAD_VALUE;
}
- if (0 != memcmp(header.magic, "keychar", 8)) {
- LOGW("Bad keycharmap magic token");
- goto cleanup1;
+#if DEBUG_PARSER
+ LOGD("Parsed beginning of key: keyCode=%d.", keyCode);
+#endif
+ mKeyCode = keyCode;
+ mMap->mKeys.add(keyCode, new Key());
+ mState = STATE_KEY;
+ return NO_ERROR;
+}
+
+status_t KeyCharacterMap::Parser::parseKeyProperty() {
+ String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
+ if (token == "}") {
+ mState = STATE_TOP;
+ return NO_ERROR;
}
- if (header.endian != 0x12345678) {
- LOGW("Bad keycharmap endians");
- goto cleanup1;
+
+ Vector<Property> properties;
+
+ // Parse all comma-delimited property names up to the first colon.
+ for (;;) {
+ if (token == "label") {
+ properties.add(Property(PROPERTY_LABEL));
+ } else if (token == "number") {
+ properties.add(Property(PROPERTY_NUMBER));
+ } else {
+ int32_t metaState;
+ status_t status = parseModifier(token, &metaState);
+ if (status) {
+ LOGE("%s: Expected a property name or modifier, got '%s'.",
+ mTokenizer->getLocation().string(), token.string());
+ return status;
+ }
+ properties.add(Property(PROPERTY_META, metaState));
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ if (!mTokenizer->isEol()) {
+ char ch = mTokenizer->nextChar();
+ if (ch == ':') {
+ break;
+ } else if (ch == ',') {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
+ continue;
+ }
+ }
+
+ LOGE("%s: Expected ',' or ':' after property name.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
}
- if ((header.version & 0xff) != 2) {
- LOGW("Only support keycharmap version 2 (got 0x%08x)", header.version);
- goto cleanup1;
+
+ // Parse behavior after the colon.
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ Behavior behavior;
+ bool haveCharacter = false;
+ bool haveFallback = false;
+
+ do {
+ char ch = mTokenizer->peekChar();
+ if (ch == '\'') {
+ char16_t character;
+ status_t status = parseCharacterLiteral(&character);
+ if (status || !character) {
+ LOGE("%s: Invalid character literal for key.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ if (haveCharacter) {
+ LOGE("%s: Cannot combine multiple character literals or 'none'.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ behavior.character = character;
+ haveCharacter = true;
+ } else {
+ token = mTokenizer->nextToken(WHITESPACE);
+ if (token == "none") {
+ if (haveCharacter) {
+ LOGE("%s: Cannot combine multiple character literals or 'none'.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ haveCharacter = true;
+ } else if (token == "fallback") {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ token = mTokenizer->nextToken(WHITESPACE);
+ int32_t keyCode = getKeyCodeByLabel(token.string());
+ if (!keyCode) {
+ LOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
+ mTokenizer->getLocation().string(),
+ token.string());
+ return BAD_VALUE;
+ }
+ if (haveFallback) {
+ LOGE("%s: Cannot combine multiple fallback key codes.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ behavior.fallbackKeyCode = keyCode;
+ haveFallback = true;
+ } else {
+ LOGE("%s: Expected a key behavior after ':'.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ } while (!mTokenizer->isEol());
+
+ // Add the behavior.
+ Key* key = mMap->mKeys.valueFor(mKeyCode);
+ for (size_t i = 0; i < properties.size(); i++) {
+ const Property& property = properties.itemAt(i);
+ switch (property.property) {
+ case PROPERTY_LABEL:
+ if (key->label) {
+ LOGE("%s: Duplicate label for key.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ key->label = behavior.character;
+#if DEBUG_PARSER
+ LOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label);
+#endif
+ break;
+ case PROPERTY_NUMBER:
+ if (key->number) {
+ LOGE("%s: Duplicate number for key.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ key->number = behavior.character;
+#if DEBUG_PARSER
+ LOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number);
+#endif
+ break;
+ case PROPERTY_META: {
+ for (Behavior* b = key->firstBehavior; b; b = b->next) {
+ if (b->metaState == property.metaState) {
+ LOGE("%s: Duplicate key behavior for modifier.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ }
+ Behavior* newBehavior = new Behavior(behavior);
+ newBehavior->metaState = property.metaState;
+ newBehavior->next = key->firstBehavior;
+ key->firstBehavior = newBehavior;
+#if DEBUG_PARSER
+ LOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode,
+ newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode);
+#endif
+ break;
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) {
+ if (token == "base") {
+ *outMetaState = 0;
+ return NO_ERROR;
+ }
+
+ int32_t combinedMeta = 0;
+
+ const char* str = token.string();
+ const char* start = str;
+ for (const char* cur = str; ; cur++) {
+ char ch = *cur;
+ if (ch == '+' || ch == '\0') {
+ size_t len = cur - start;
+ int32_t metaState = 0;
+ for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) {
+ if (strlen(modifiers[i].label) == len
+ && strncmp(modifiers[i].label, start, len) == 0) {
+ metaState = modifiers[i].metaState;
+ break;
+ }
+ }
+ if (!metaState) {
+ return BAD_VALUE;
+ }
+ if (combinedMeta & metaState) {
+ LOGE("%s: Duplicate modifier combination '%s'.",
+ mTokenizer->getLocation().string(), token.string());
+ return BAD_VALUE;
+ }
+
+ combinedMeta |= metaState;
+
+ if (ch == '\0') {
+ break;
+ }
+ }
}
- if (filesize < (off_t)(sizeof(Header)+(sizeof(Key)*header.keycount))) {
- LOGW("Bad keycharmap file size\n");
- goto cleanup1;
+ *outMetaState = combinedMeta;
+ return NO_ERROR;
+}
+
+status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) {
+ char ch = mTokenizer->nextChar();
+ if (ch != '\'') {
+ goto Error;
}
- // read the key data
- keys = (Key*)malloc(sizeof(Key)*header.keycount);
- err = read(fd, keys, sizeof(Key)*header.keycount);
- if (err == -1) {
- LOGW("Error reading keycharmap file");
- free(keys);
- goto cleanup1;
+ ch = mTokenizer->nextChar();
+ if (ch == '\\') {
+ // Escape sequence.
+ ch = mTokenizer->nextChar();
+ if (ch == 'n') {
+ *outCharacter = '\n';
+ } else if (ch == 't') {
+ *outCharacter = '\t';
+ } else if (ch == '\\') {
+ *outCharacter = '\\';
+ } else if (ch == '\'') {
+ *outCharacter = '\'';
+ } else if (ch == '"') {
+ *outCharacter = '"';
+ } else if (ch == 'u') {
+ *outCharacter = 0;
+ for (int i = 0; i < 4; i++) {
+ ch = mTokenizer->nextChar();
+ int digit;
+ if (ch >= '0' && ch <= '9') {
+ digit = ch - '0';
+ } else if (ch >= 'A' && ch <= 'F') {
+ digit = ch - 'A' + 10;
+ } else if (ch >= 'a' && ch <= 'f') {
+ digit = ch - 'a' + 10;
+ } else {
+ goto Error;
+ }
+ *outCharacter = (*outCharacter << 4) | digit;
+ }
+ } else {
+ goto Error;
+ }
+ } else if (ch >= 32 && ch <= 126 && ch != '\'') {
+ // ASCII literal character.
+ *outCharacter = ch;
+ } else {
+ goto Error;
}
- // return the object
- rv = new KeyCharacterMap;
- rv->m_keyCount = header.keycount;
- rv->m_keys = keys;
- rv->m_type = header.kbdtype;
+ ch = mTokenizer->nextChar();
+ if (ch != '\'') {
+ goto Error;
+ }
-cleanup1:
- close(fd);
+ // Ensure that we consumed the entire token.
+ if (mTokenizer->nextToken(WHITESPACE).isEmpty()) {
+ return NO_ERROR;
+ }
- return rv;
+Error:
+ LOGE("%s: Malformed character literal.", mTokenizer->getLocation().string());
+ return BAD_VALUE;
}
+
+} // namespace android
diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/ui/KeyLayoutMap.cpp
index 15ae54c..56bc26f 100644
--- a/libs/ui/KeyLayoutMap.cpp
+++ b/libs/ui/KeyLayoutMap.cpp
@@ -1,234 +1,213 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#define LOG_TAG "KeyLayoutMap"
-#include "KeyLayoutMap.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <utils/String8.h>
#include <stdlib.h>
-#include <ui/KeycodeLabels.h>
+#include <android/keycodes.h>
+#include <ui/Keyboard.h>
+#include <ui/KeyLayoutMap.h>
#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/Tokenizer.h>
+#include <utils/Timers.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+// Enables debug output for mapping.
+#define DEBUG_MAPPING 0
+
namespace android {
-KeyLayoutMap::KeyLayoutMap()
- :m_status(NO_INIT),
- m_keys()
-{
-}
+static const char* WHITESPACE = " \t\r";
-KeyLayoutMap::~KeyLayoutMap()
-{
+// --- KeyLayoutMap ---
+
+KeyLayoutMap::KeyLayoutMap() {
}
-static String8
-next_token(char const** p, int *line)
-{
- bool begun = false;
- const char* begin = *p;
- const char* end = *p;
- while (true) {
- if (*end == '\n') {
- (*line)++;
- }
- switch (*end)
- {
- case '#':
- if (begun) {
- *p = end;
- return String8(begin, end-begin);
- } else {
- do {
- begin++;
- end++;
- } while (*begin != '\0' && *begin != '\n');
- }
- case '\0':
- case ' ':
- case '\n':
- case '\r':
- case '\t':
- if (begun || (*end == '\0')) {
- *p = end;
- return String8(begin, end-begin);
- } else {
- begin++;
- end++;
- break;
- }
- default:
- end++;
- begun = true;
- }
- }
+KeyLayoutMap::~KeyLayoutMap() {
}
-static int32_t
-token_to_value(const char *literal, const KeycodeLabel *list)
-{
- while (list->literal) {
- if (0 == strcmp(literal, list->literal)) {
- return list->value;
+status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) {
+ *outMap = NULL;
+
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::open(filename, &tokenizer);
+ if (status) {
+ LOGE("Error %d opening key layout map file %s.", status, filename.string());
+ } else {
+ KeyLayoutMap* map = new KeyLayoutMap();
+ if (!map) {
+ LOGE("Error allocating key layout map.");
+ status = NO_MEMORY;
+ } else {
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map, tokenizer);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ LOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ elapsedTime / 1000000.0);
+#endif
+ if (status) {
+ delete map;
+ } else {
+ *outMap = map;
+ }
}
- list++;
+ delete tokenizer;
}
- return list->value;
+ return status;
}
-status_t
-KeyLayoutMap::load(const char* filename)
-{
- int fd = open(filename, O_RDONLY);
- if (fd < 0) {
- LOGE("error opening file=%s err=%s\n", filename, strerror(errno));
- m_status = errno;
- return errno;
+status_t KeyLayoutMap::map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const {
+ ssize_t index = mKeys.indexOfKey(scanCode);
+ if (index < 0) {
+#if DEBUG_MAPPING
+ LOGD("map: scanCode=%d ~ Failed.", scanCode);
+#endif
+ *keyCode = AKEYCODE_UNKNOWN;
+ *flags = 0;
+ return NAME_NOT_FOUND;
}
- off_t len = lseek(fd, 0, SEEK_END);
- off_t errlen = lseek(fd, 0, SEEK_SET);
- if (len < 0 || errlen < 0) {
- close(fd);
- LOGE("error seeking file=%s err=%s\n", filename, strerror(errno));
- m_status = errno;
- return errno;
- }
+ const Key& k = mKeys.valueAt(index);
+ *keyCode = k.keyCode;
+ *flags = k.flags;
- char* buf = (char*)malloc(len+1);
- if (read(fd, buf, len) != len) {
- LOGE("error reading file=%s err=%s\n", filename, strerror(errno));
- m_status = errno != 0 ? errno : ((int)NOT_ENOUGH_DATA);
- return errno != 0 ? errno : ((int)NOT_ENOUGH_DATA);
- }
- errno = 0;
- buf[len] = '\0';
+#if DEBUG_MAPPING
+ LOGD("map: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags);
+#endif
+ return NO_ERROR;
+}
- int32_t scancode = -1;
- int32_t keycode = -1;
- uint32_t flags = 0;
- uint32_t tmp;
- char* end;
- status_t err = NO_ERROR;
- int line = 1;
- char const* p = buf;
- enum { BEGIN, SCANCODE, KEYCODE, FLAG } state = BEGIN;
- while (true) {
- String8 token = next_token(&p, &line);
- if (*p == '\0') {
- break;
- }
- switch (state)
- {
- case BEGIN:
- if (token == "key") {
- state = SCANCODE;
- } else {
- LOGE("%s:%d: expected key, got '%s'\n", filename, line,
- token.string());
- err = BAD_VALUE;
- goto done;
- }
- break;
- case SCANCODE:
- scancode = strtol(token.string(), &end, 0);
- if (*end != '\0') {
- LOGE("%s:%d: expected scancode (a number), got '%s'\n",
- filename, line, token.string());
- goto done;
- }
- //LOGI("%s:%d: got scancode %d\n", filename, line, scancode );
- state = KEYCODE;
- break;
- case KEYCODE:
- keycode = token_to_value(token.string(), KEYCODES);
- //LOGI("%s:%d: got keycode %d for %s\n", filename, line, keycode, token.string() );
- if (keycode == 0) {
- LOGE("%s:%d: expected keycode, got '%s'\n",
- filename, line, token.string());
- goto done;
- }
- state = FLAG;
- break;
- case FLAG:
- if (token == "key") {
- if (scancode != -1) {
- //LOGI("got key decl scancode=%d keycode=%d"
- // " flags=0x%08x\n", scancode, keycode, flags);
- Key k = { keycode, flags };
- m_keys.add(scancode, k);
- state = SCANCODE;
- scancode = -1;
- keycode = -1;
- flags = 0;
- break;
- }
- }
- tmp = token_to_value(token.string(), FLAGS);
- //LOGI("%s:%d: got flags %x for %s\n", filename, line, tmp, token.string() );
- if (tmp == 0) {
- LOGE("%s:%d: expected flag, got '%s'\n",
- filename, line, token.string());
- goto done;
- }
- flags |= tmp;
- break;
+status_t KeyLayoutMap::findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
+ const size_t N = mKeys.size();
+ for (size_t i=0; i<N; i++) {
+ if (mKeys.valueAt(i).keyCode == keyCode) {
+ outScanCodes->add(mKeys.keyAt(i));
}
}
- if (state == FLAG && scancode != -1 ) {
- //LOGI("got key decl scancode=%d keycode=%d"
- // " flags=0x%08x\n", scancode, keycode, flags);
- Key k = { keycode, flags };
- m_keys.add(scancode, k);
- }
-
-done:
- free(buf);
- close(fd);
-
- m_status = err;
- return err;
+ return NO_ERROR;
}
-status_t
-KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags) const
-{
- if (m_status != NO_ERROR) {
- return m_status;
- }
+// --- KeyLayoutMap::Parser ---
- ssize_t index = m_keys.indexOfKey(scancode);
- if (index < 0) {
- //LOGW("couldn't map scancode=%d\n", scancode);
- return NAME_NOT_FOUND;
- }
-
- const Key& k = m_keys.valueAt(index);
+KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
+ mMap(map), mTokenizer(tokenizer) {
+}
- *keycode = k.keycode;
- *flags = k.flags;
+KeyLayoutMap::Parser::~Parser() {
+}
- //LOGD("mapped scancode=%d to keycode=%d flags=0x%08x\n", scancode,
- // keycode, flags);
+status_t KeyLayoutMap::Parser::parse() {
+ while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+ LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+#endif
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+ String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
+ if (keywordToken == "key") {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ status_t status = parseKey();
+ if (status) return status;
+ } else {
+ LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
+ keywordToken.string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ if (!mTokenizer->isEol()) {
+ LOGE("%s: Expected end of line, got '%s'.",
+ mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+ return BAD_VALUE;
+ }
+ }
+ mTokenizer->nextLine();
+ }
return NO_ERROR;
}
-status_t
-KeyLayoutMap::findScancodes(int32_t keycode, Vector<int32_t>* outScancodes) const
-{
- if (m_status != NO_ERROR) {
- return m_status;
+status_t KeyLayoutMap::Parser::parseKey() {
+ String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
+ char* end;
+ int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
+ if (*end) {
+ LOGE("%s: Expected scan code number, got '%s'.", mTokenizer->getLocation().string(),
+ scanCodeToken.string());
+ return BAD_VALUE;
}
-
- const size_t N = m_keys.size();
- for (size_t i=0; i<N; i++) {
- if (m_keys.valueAt(i).keycode == keycode) {
- outScancodes->add(m_keys.keyAt(i));
+ if (mMap->mKeys.indexOfKey(scanCode) >= 0) {
+ LOGE("%s: Duplicate entry for scan code '%s'.", mTokenizer->getLocation().string(),
+ scanCodeToken.string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
+ int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+ if (!keyCode) {
+ LOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
+ keyCodeToken.string());
+ return BAD_VALUE;
+ }
+
+ uint32_t flags = 0;
+ for (;;) {
+ mTokenizer->skipDelimiters(WHITESPACE);
+ if (mTokenizer->isEol()) break;
+
+ String8 flagToken = mTokenizer->nextToken(WHITESPACE);
+ uint32_t flag = getKeyFlagByLabel(flagToken.string());
+ if (!flag) {
+ LOGE("%s: Expected flag label, got '%s'.", mTokenizer->getLocation().string(),
+ flagToken.string());
+ return BAD_VALUE;
}
+ if (flags & flag) {
+ LOGE("%s: Duplicate flag '%s'.", mTokenizer->getLocation().string(),
+ flagToken.string());
+ return BAD_VALUE;
+ }
+ flags |= flag;
}
-
+
+#if DEBUG_PARSER
+ LOGD("Parsed key: scanCode=%d, keyCode=%d, flags=0x%08x.", scanCode, keyCode, flags);
+#endif
+ Key key;
+ key.keyCode = keyCode;
+ key.flags = flags;
+ mMap->mKeys.add(scanCode, key);
return NO_ERROR;
}
diff --git a/libs/ui/KeyLayoutMap.h b/libs/ui/KeyLayoutMap.h
deleted file mode 100644
index 43f84ce..0000000
--- a/libs/ui/KeyLayoutMap.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef KEYLAYOUTMAP_H
-#define KEYLAYOUTMAP_H
-
-#include <utils/KeyedVector.h>
-
-namespace android {
-
-class KeyLayoutMap
-{
-public:
- KeyLayoutMap();
- ~KeyLayoutMap();
-
- status_t load(const char* filename);
-
- status_t map(int32_t scancode, int32_t *keycode, uint32_t *flags) const;
- status_t findScancodes(int32_t keycode, Vector<int32_t>* outScancodes) const;
-
-private:
- struct Key {
- int32_t keycode;
- uint32_t flags;
- };
-
- status_t m_status;
- KeyedVector<int32_t,Key> m_keys;
-};
-
-};
-
-#endif // KEYLAYOUTMAP_H
diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp
new file mode 100644
index 0000000..de76e25
--- /dev/null
+++ b/libs/ui/Keyboard.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Keyboard"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <ui/Keyboard.h>
+#include <ui/KeycodeLabels.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <cutils/properties.h>
+
+namespace android {
+
+static void selectKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) {
+ if (keyMapInfo.keyMapName.isEmpty()) {
+ keyMapInfo.keyMapName.setTo(keyMapName);
+ keyMapInfo.isDefaultKeyMap = defaultKeyMap;
+ }
+}
+
+static bool probeKeyMap(KeyMapInfo& keyMapInfo, const String8& keyMapName, bool defaultKeyMap) {
+ const char* root = getenv("ANDROID_ROOT");
+
+ // TODO Consider also looking somewhere in a writeable partition like /data for a
+ // custom keymap supplied by the user for this device.
+ bool haveKeyLayout = !keyMapInfo.keyLayoutFile.isEmpty();
+ if (!haveKeyLayout) {
+ keyMapInfo.keyLayoutFile.setTo(root);
+ keyMapInfo.keyLayoutFile.append("/usr/keylayout/");
+ keyMapInfo.keyLayoutFile.append(keyMapName);
+ keyMapInfo.keyLayoutFile.append(".kl");
+ if (access(keyMapInfo.keyLayoutFile.string(), R_OK)) {
+ keyMapInfo.keyLayoutFile.clear();
+ } else {
+ haveKeyLayout = true;
+ }
+ }
+
+ bool haveKeyCharacterMap = !keyMapInfo.keyCharacterMapFile.isEmpty();
+ if (!haveKeyCharacterMap) {
+ keyMapInfo.keyCharacterMapFile.setTo(root);
+ keyMapInfo.keyCharacterMapFile.append("/usr/keychars/");
+ keyMapInfo.keyCharacterMapFile.append(keyMapName);
+ keyMapInfo.keyCharacterMapFile.append(".kcm");
+ if (access(keyMapInfo.keyCharacterMapFile.string(), R_OK)) {
+ keyMapInfo.keyCharacterMapFile.clear();
+ } else {
+ haveKeyCharacterMap = true;
+ }
+ }
+
+ if (haveKeyLayout || haveKeyCharacterMap) {
+ selectKeyMap(keyMapInfo, keyMapName, defaultKeyMap);
+ }
+ return haveKeyLayout && haveKeyCharacterMap;
+}
+
+status_t resolveKeyMap(const String8& deviceName, KeyMapInfo& outKeyMapInfo) {
+ // As an initial key map name, try using the device name.
+ String8 keyMapName(deviceName);
+ char* p = keyMapName.lockBuffer(keyMapName.size());
+ while (*p) {
+ if (*p == ' ') *p = '_';
+ p++;
+ }
+ keyMapName.unlockBuffer();
+
+ if (probeKeyMap(outKeyMapInfo, keyMapName, false)) return OK;
+
+ // TODO Consider allowing the user to configure a specific key map somehow.
+
+ // Try the Generic key map.
+ // TODO Apply some additional heuristics here to figure out what kind of
+ // generic key map to use (US English, etc.).
+ keyMapName.setTo("Generic");
+ if (probeKeyMap(outKeyMapInfo, keyMapName, true)) return OK;
+
+ // Give up!
+ keyMapName.setTo("unknown");
+ selectKeyMap(outKeyMapInfo, keyMapName, true);
+ LOGE("Could not determine key map for device '%s'.", deviceName.string());
+ return NAME_NOT_FOUND;
+}
+
+void setKeyboardProperties(int32_t deviceId, const String8& deviceName,
+ const KeyMapInfo& keyMapInfo) {
+ char propName[PROPERTY_KEY_MAX];
+ snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
+ property_set(propName, deviceName.string());
+ snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId);
+ property_set(propName, keyMapInfo.keyMapName.string());
+ snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
+ property_set(propName, keyMapInfo.keyLayoutFile.string());
+ snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
+ property_set(propName, keyMapInfo.keyCharacterMapFile.string());
+}
+
+void clearKeyboardProperties(int32_t deviceId) {
+ char propName[PROPERTY_KEY_MAX];
+ snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId);
+ property_set(propName, "");
+ snprintf(propName, sizeof(propName), "hw.keyboards.%u.keymap", deviceId);
+ property_set(propName, "");
+ snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId);
+ property_set(propName, "");
+ snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
+ property_set(propName, "");
+}
+
+status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) {
+ char propName[PROPERTY_KEY_MAX];
+ char fn[PROPERTY_VALUE_MAX];
+ snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId);
+ if (property_get(propName, fn, "") > 0) {
+ outKeyCharacterMapFile.setTo(fn);
+ return OK;
+ }
+
+ const char* root = getenv("ANDROID_ROOT");
+ char path[PATH_MAX];
+ if (deviceId == DEVICE_ID_VIRTUAL_KEYBOARD) {
+ snprintf(path, sizeof(path), "%s/usr/keychars/Virtual.kcm", root);
+ if (!access(path, R_OK)) {
+ outKeyCharacterMapFile.setTo(path);
+ return OK;
+ }
+ }
+
+ snprintf(path, sizeof(path), "%s/usr/keychars/Generic.kcm", root);
+ if (!access(path, R_OK)) {
+ outKeyCharacterMapFile.setTo(path);
+ return OK;
+ }
+
+ LOGE("Can't find any key character map files (also tried %s)", path);
+ return NAME_NOT_FOUND;
+}
+
+static int lookupLabel(const char* literal, const KeycodeLabel *list) {
+ while (list->literal) {
+ if (strcmp(literal, list->literal) == 0) {
+ return list->value;
+ }
+ list++;
+ }
+ return list->value;
+}
+
+int32_t getKeyCodeByLabel(const char* label) {
+ return int32_t(lookupLabel(label, KEYCODES));
+}
+
+uint32_t getKeyFlagByLabel(const char* label) {
+ return uint32_t(lookupLabel(label, FLAGS));
+}
+
+static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
+ int32_t newMetaState;
+ if (down) {
+ newMetaState = oldMetaState | mask;
+ } else {
+ newMetaState = oldMetaState &
+ ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
+ }
+
+ if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
+ newMetaState |= AMETA_ALT_ON;
+ }
+
+ if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
+ newMetaState |= AMETA_SHIFT_ON;
+ }
+
+ if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
+ newMetaState |= AMETA_CTRL_ON;
+ }
+
+ if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
+ newMetaState |= AMETA_META_ON;
+ }
+ return newMetaState;
+}
+
+static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
+ if (down) {
+ return oldMetaState;
+ } else {
+ return oldMetaState ^ mask;
+ }
+}
+
+int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
+ int32_t mask;
+ switch (keyCode) {
+ case AKEYCODE_ALT_LEFT:
+ return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
+ case AKEYCODE_ALT_RIGHT:
+ return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
+ case AKEYCODE_SHIFT_LEFT:
+ return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
+ case AKEYCODE_SHIFT_RIGHT:
+ return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
+ case AKEYCODE_SYM:
+ return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
+ case AKEYCODE_FUNCTION:
+ return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
+ case AKEYCODE_CTRL_LEFT:
+ return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
+ case AKEYCODE_CTRL_RIGHT:
+ return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
+ case AKEYCODE_META_LEFT:
+ return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
+ case AKEYCODE_META_RIGHT:
+ return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
+ case AKEYCODE_CAPS_LOCK:
+ return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
+ case AKEYCODE_NUM_LOCK:
+ return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
+ case AKEYCODE_SCROLL_LOCK:
+ return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
+ default:
+ return oldMetaState;
+ }
+}
+
+
+} // namespace android
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 05a9674..9c01aea 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -41,6 +41,7 @@ commonSources:= \
TextOutput.cpp \
Threads.cpp \
Timers.cpp \
+ Tokenizer.cpp \
Unicode.cpp \
VectorImpl.cpp \
ZipFileCRO.cpp \
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index c8dc083..e531a2a 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -282,22 +282,28 @@ status_t String8::append(const char* other, size_t otherLen)
status_t String8::appendFormat(const char* fmt, ...)
{
- va_list ap;
- va_start(ap, fmt);
+ va_list args;
+ va_start(args, fmt);
+ status_t result = appendFormatV(fmt, args);
+
+ va_end(args);
+ return result;
+}
+
+status_t String8::appendFormatV(const char* fmt, va_list args)
+{
int result = NO_ERROR;
- int n = vsnprintf(NULL, 0, fmt, ap);
+ int n = vsnprintf(NULL, 0, fmt, args);
if (n != 0) {
size_t oldLength = length();
char* buf = lockBuffer(oldLength + n);
if (buf) {
- vsnprintf(buf + oldLength, n + 1, fmt, ap);
+ vsnprintf(buf + oldLength, n + 1, fmt, args);
} else {
result = NO_MEMORY;
}
}
-
- va_end(ap);
return result;
}
diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp
new file mode 100644
index 0000000..19dadf0
--- /dev/null
+++ b/libs/utils/Tokenizer.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Tokenizer"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <utils/Log.h>
+#include <utils/Tokenizer.h>
+
+// Enables debug output for the tokenizer.
+#define DEBUG_TOKENIZER 0
+
+
+namespace android {
+
+static inline bool isDelimiter(char ch, const char* delimiters) {
+ return strchr(delimiters, ch) != NULL;
+}
+
+
+Tokenizer::Tokenizer(const String8& filename, const char* buffer, size_t length) :
+ mFilename(filename), mBuffer(buffer), mLength(length),
+ mCurrent(buffer), mLineNumber(1) {
+}
+
+Tokenizer::~Tokenizer() {
+ munmap((void*)mBuffer, mLength);
+}
+
+status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
+ *outTokenizer = NULL;
+
+ int result = NO_ERROR;
+ int fd = ::open(filename.string(), O_RDONLY);
+ if (fd < 0) {
+ result = -errno;
+ LOGE("Error opening file '%s', %s.", filename.string(), strerror(errno));
+ } else {
+ struct stat64 stat;
+ if (fstat64(fd, &stat)) {
+ result = -errno;
+ LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno));
+ } else {
+ size_t length = size_t(stat.st_size);
+ void* buffer = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+ if (buffer == MAP_FAILED) {
+ result = -errno;
+ LOGE("Error mapping file '%s', %s.", filename.string(), strerror(errno));
+ } else {
+ if (madvise(buffer, length, MADV_SEQUENTIAL)) {
+ LOGW("Error calling madvise for mmapped file '%s', %s.", filename.string(),
+ strerror(errno));
+ }
+
+ *outTokenizer = new Tokenizer(filename, static_cast<const char*>(buffer), length);
+ if (!*outTokenizer) {
+ result = NO_MEMORY;
+ LOGE("Error allocating tokenizer for file=%s.", filename.string());
+ munmap(buffer, length);
+ }
+ }
+ }
+ close(fd);
+ }
+ return result;
+}
+
+String8 Tokenizer::getLocation() const {
+ String8 result;
+ result.appendFormat("%s:%d", mFilename.string(), mLineNumber);
+ return result;
+}
+
+String8 Tokenizer::peekRemainderOfLine() const {
+ const char* end = getEnd();
+ const char* eol = mCurrent;
+ while (eol != end) {
+ char ch = *eol;
+ if (ch == '\n') {
+ break;
+ }
+ eol += 1;
+ }
+ return String8(mCurrent, eol - mCurrent);
+}
+
+String8 Tokenizer::nextToken(const char* delimiters) {
+#if DEBUG_TOKENIZER
+ LOGD("nextToken");
+#endif
+ const char* end = getEnd();
+ const char* tokenStart = mCurrent;
+ while (mCurrent != end) {
+ char ch = *mCurrent;
+ if (ch == '\n' || isDelimiter(ch, delimiters)) {
+ break;
+ }
+ mCurrent += 1;
+ }
+ return String8(tokenStart, mCurrent - tokenStart);
+}
+
+void Tokenizer::nextLine() {
+#if DEBUG_TOKENIZER
+ LOGD("nextLine");
+#endif
+ const char* end = getEnd();
+ while (mCurrent != end) {
+ char ch = *(mCurrent++);
+ if (ch == '\n') {
+ mLineNumber += 1;
+ break;
+ }
+ }
+}
+
+void Tokenizer::skipDelimiters(const char* delimiters) {
+#if DEBUG_TOKENIZER
+ LOGD("skipDelimiters");
+#endif
+ const char* end = getEnd();
+ while (mCurrent != end) {
+ char ch = *mCurrent;
+ if (ch == '\n' || !isDelimiter(ch, delimiters)) {
+ break;
+ }
+ mCurrent += 1;
+ }
+}
+
+} // namespace android
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index aed3dc7..5374957 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -28,6 +28,7 @@ import android.util.Slog;
import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
import android.view.InputDevice;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -133,7 +134,7 @@ public class KeyButtonView extends ImageView {
void sendEvent(int action, int flags, long when) {
final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, mRepeat,
- 0, 0, 0, flags, InputDevice.SOURCE_KEYBOARD);
+ 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, flags, InputDevice.SOURCE_KEYBOARD);
try {
//Slog.d(TAG, "injecting event " + ev);
mWindowManager.injectInputEventNoWait(ev);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 18815f5..cd8a065 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -349,7 +349,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
// Set the proper keymap
- KeyCharacterMap kmap = KeyCharacterMap.load(event != null ? event.getDeviceId() : 0);
+ KeyCharacterMap kmap = KeyCharacterMap.load(
+ event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC;
st.menu.setQwertyMode(st.qwertyMode);
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 8c857da..cc391d5 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -67,6 +67,7 @@ import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputQueue;
import android.view.InputHandler;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowOrientationListener;
@@ -2126,7 +2127,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// only do it if the showing app doesn't process the key on its own.
long when = whenNanos / 1000000;
KeyEvent keyEvent = new KeyEvent(when, when, action, keyCode, 0, 0,
- 0, scanCode, flags, InputDevice.SOURCE_KEYBOARD);
+ KeyCharacterMap.VIRTUAL_KEYBOARD, scanCode, flags,
+ InputDevice.SOURCE_KEYBOARD);
mBroadcastWakeLock.acquire();
mHandler.post(new PassHeadsetKey(keyEvent));
}
diff --git a/policy/src/com/android/internal/policy/impl/ShortcutManager.java b/policy/src/com/android/internal/policy/impl/ShortcutManager.java
index d86ac44..51377d8 100644
--- a/policy/src/com/android/internal/policy/impl/ShortcutManager.java
+++ b/policy/src/com/android/internal/policy/impl/ShortcutManager.java
@@ -106,7 +106,7 @@ class ShortcutManager extends ContentObserver {
* @return The intent that matches the shortcut, or null if not found.
*/
public Intent getIntent(int keyCode, int modifiers) {
- KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+ KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
// First try the exact keycode (with modifiers)
int shortcut = kcm.get(keyCode, modifiers);
Intent intent = shortcut != 0 ? mShortcutIntents.get(shortcut) : null;