summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.xml22
-rw-r--r--core/java/android/view/KeyCharacterMap.java61
-rwxr-xr-xcore/java/android/view/KeyEvent.java19
-rw-r--r--core/java/android/view/View.java2
-rw-r--r--core/java/android/view/ViewRoot.java28
-rw-r--r--core/java/android/view/WindowManagerPolicy.java5
-rw-r--r--core/jni/Android.mk2
-rw-r--r--core/jni/android_view_KeyCharacterMap.cpp (renamed from core/jni/android_text_KeyCharacterMap.cpp)37
-rwxr-xr-xcore/res/res/values/attrs.xml1
-rw-r--r--data/keyboards/Generic.kcm38
-rw-r--r--data/keyboards/Vendor_05ac_Product_0239.kl2
-rw-r--r--data/keyboards/Vendor_22b8_Product_093d.kl4
-rw-r--r--data/keyboards/Virtual.kcm38
-rw-r--r--include/ui/InputDispatcher.h12
-rw-r--r--include/ui/InputReader.h3
-rw-r--r--include/ui/InputTransport.h2
-rw-r--r--include/ui/KeyCharacterMap.h17
-rwxr-xr-xinclude/ui/KeycodeLabels.h1
-rw-r--r--libs/ui/InputDispatcher.cpp88
-rw-r--r--libs/ui/InputReader.cpp33
-rw-r--r--libs/ui/InputTransport.cpp8
-rw-r--r--libs/ui/KeyCharacterMap.cpp79
-rw-r--r--libs/ui/tests/InputDispatcher_test.cpp2
-rw-r--r--libs/ui/tests/InputPublisherAndConsumer_test.cpp4
-rw-r--r--libs/ui/tests/InputReader_test.cpp11
-rw-r--r--native/include/android/input.h10
-rw-r--r--native/include/android/keycodes.h1
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java145
-rw-r--r--services/java/com/android/server/InputManager.java2
-rw-r--r--services/java/com/android/server/WindowManagerService.java2
-rw-r--r--services/jni/com_android_server_InputManager.cpp34
31 files changed, 529 insertions, 184 deletions
diff --git a/api/current.xml b/api/current.xml
index b90ac5f..83230f0 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -198463,6 +198463,17 @@
visibility="public"
>
</field>
+<field name="FLAG_FALLBACK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1024"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_FROM_SYSTEM"
type="int"
transient="false"
@@ -198694,6 +198705,17 @@
visibility="public"
>
</field>
+<field name="KEYCODE_APP_SWITCH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="187"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="KEYCODE_AT"
type="int"
transient="false"
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 97bd8dd..5c4abd5 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -144,6 +144,8 @@ public class KeyCharacterMap {
private static native void nativeDispose(int ptr);
private static native char nativeGetCharacter(int ptr, int keyCode, int metaState);
+ private static native boolean nativeGetFallbackAction(int ptr, int keyCode, int metaState,
+ FallbackAction outFallbackAction);
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);
@@ -206,14 +208,9 @@ public class KeyCharacterMap {
* @return The associated character or combining accent, or 0 if none.
*/
public int get(int keyCode, int metaState) {
- if ((metaState & MetaKeyKeyListener.META_CAP_LOCKED) != 0) {
- metaState |= KeyEvent.META_CAPS_LOCK_ON;
- }
- if ((metaState & MetaKeyKeyListener.META_ALT_LOCKED) != 0) {
- metaState |= KeyEvent.META_ALT_ON;
- }
-
+ metaState = applyLockedModifiers(metaState);
char ch = nativeGetCharacter(mPtr, keyCode, metaState);
+
int map = COMBINING.get(ch);
if (map != 0) {
return map;
@@ -223,6 +220,34 @@ public class KeyCharacterMap {
}
/**
+ * Gets the fallback action to perform if the application does not
+ * handle the specified key.
+ * <p>
+ * When an application does not handle a particular key, the system may
+ * translate the key to an alternate fallback key (specified in the
+ * fallback action) and dispatch it to the application.
+ * The event containing the fallback key is flagged
+ * with {@link KeyEvent#FLAG_FALLBACK}.
+ * </p>
+ *
+ * @param keyCode The key code.
+ * @param metaState The meta key modifier state.
+ * @param outFallbackAction The fallback action object to populate.
+ * @return True if a fallback action was found, false otherwise.
+ *
+ * @hide
+ */
+ public boolean getFallbackAction(int keyCode, int metaState,
+ FallbackAction outFallbackAction) {
+ if (outFallbackAction == null) {
+ throw new IllegalArgumentException("fallbackAction must not be null");
+ }
+
+ metaState = applyLockedModifiers(metaState);
+ return nativeGetFallbackAction(mPtr, keyCode, metaState, outFallbackAction);
+ }
+
+ /**
* Gets the number or symbol associated with the key.
* <p>
* The character value is returned, not the numeric value.
@@ -277,6 +302,8 @@ public class KeyCharacterMap {
if (chars == null) {
throw new IllegalArgumentException("chars must not be null.");
}
+
+ metaState = applyLockedModifiers(metaState);
return nativeGetMatch(mPtr, keyCode, chars, metaState);
}
@@ -509,6 +536,16 @@ public class KeyCharacterMap {
return ret;
}
+ private static int applyLockedModifiers(int metaState) {
+ if ((metaState & MetaKeyKeyListener.META_CAP_LOCKED) != 0) {
+ metaState |= KeyEvent.META_CAPS_LOCK_ON;
+ }
+ if ((metaState & MetaKeyKeyListener.META_ALT_LOCKED) != 0) {
+ metaState |= KeyEvent.META_ALT_ON;
+ }
+ return metaState;
+ }
+
/**
* Maps Unicode combining diacritical to display-form dead key
* (display character shifted left 16 bits).
@@ -670,4 +707,14 @@ public class KeyCharacterMap {
super(msg);
}
}
+
+ /**
+ * Specifies a substitute key code and meta state as a fallback action
+ * for an unhandled key.
+ * @hide
+ */
+ public static final class FallbackAction {
+ public int keyCode;
+ public int metaState;
+ }
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 03407a3..43b77e6 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -529,15 +529,17 @@ public class KeyEvent extends InputEvent implements Parcelable {
/** Key code constant: Blue "programmable" key.
* On TV remotes, acts as a contextual/programmable key. */
public static final int KEYCODE_PROG_BLUE = 186;
+ /** Key code constant: App switch key.
+ * Should bring up the application switcher dialog. */
+ public static final int KEYCODE_APP_SWITCH = 187;
- private static final int LAST_KEYCODE = KEYCODE_PROG_BLUE;
+ private static final int LAST_KEYCODE = KEYCODE_APP_SWITCH;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
// native/include/android/keycodes.h
// frameworks/base/include/ui/KeycodeLabels.h
// external/webkit/WebKit/android/plugins/ANPKeyCodes.h
- // tools/puppet_master/PuppetMaster/nav_keys.py
// frameworks/base/core/res/res/values/attrs.xml
// emulator?
//
@@ -737,6 +739,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
"KEYCODE_PROG_GREEN",
"KEYCODE_PROG_YELLOW",
"KEYCODE_PROG_BLUE",
+ "KEYCODE_APP_SWITCH",
};
// Symbolic names of all metakeys in bit order from least significant to most significant.
@@ -1056,7 +1059,17 @@ public class KeyEvent extends InputEvent implements Parcelable {
* the tracking to be canceled.
*/
public static final int FLAG_TRACKING = 0x200;
-
+
+ /**
+ * Set when a key event has been synthesized to implement default behavior
+ * for an event that the application did not handle.
+ * Fallback key events are generated by unhandled trackball motions
+ * (to emulate a directional keypad) and by certain unhandled key presses
+ * that are declared in the key map (such as special function numeric keypad
+ * keys when numlock is off).
+ */
+ public static final int FLAG_FALLBACK = 0x400;
+
/**
* Private control to determine when an app is tracking a key sequence.
* @hide
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1f5bb5d..ad96686 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -556,7 +556,7 @@ import java.util.WeakHashMap;
* improve the security of views that provide access to sensitive functionality.
* </p><p>
* To enable touch filtering, call {@link #setFilterTouchesWhenObscured} or set the
- * andoird:filterTouchesWhenObscured attribute to true. When enabled, the framework
+ * android:filterTouchesWhenObscured layout attribute to true. When enabled, the framework
* will discard touches that are received whenever the view's window is obscured by
* another visible window. As a result, the view will not receive touches whenever a
* toast, dialog or other window appears above the view's window.
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 53cff91..9bda637 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -2310,21 +2310,23 @@ public final class ViewRoot extends Handler implements ViewParent,
}
final int action = event.getAction();
- final int metastate = event.getMetaState();
+ final int metaState = event.getMetaState();
switch (action) {
case MotionEvent.ACTION_DOWN:
x.reset(2);
y.reset(2);
deliverKeyEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER,
- 0, metastate), false);
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD), false);
break;
case MotionEvent.ACTION_UP:
x.reset(2);
y.reset(2);
deliverKeyEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER,
- 0, metastate), false);
+ KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD), false);
break;
}
@@ -2374,9 +2376,11 @@ public final class ViewRoot extends Handler implements ViewParent,
if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
+ keycode);
movement--;
+ int repeatCount = accelMovement - movement;
deliverKeyEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_MULTIPLE, keycode,
- accelMovement-movement, metastate), false);
+ KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD), false);
}
while (movement > 0) {
if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
@@ -2384,10 +2388,14 @@ public final class ViewRoot extends Handler implements ViewParent,
movement--;
curTime = SystemClock.uptimeMillis();
deliverKeyEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_DOWN, keycode, 0, event.getMetaState()), false);
+ KeyEvent.ACTION_DOWN, keycode, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD), false);
deliverKeyEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_UP, keycode, 0, metastate), false);
- }
+ KeyEvent.ACTION_UP, keycode, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD), false);
+ }
mLastTrackballTime = curTime;
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index af36d80..3479bf5 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -587,9 +587,10 @@ public interface WindowManagerPolicy {
* event will normally go.
* @param event The key event.
* @param policyFlags The policy flags associated with the key.
- * @return Returns true if the policy consumed the event.
+ * @return Returns an alternate key event to redispatch as a fallback, or null to give up.
+ * The caller is responsible for recycling the key event.
*/
- public boolean dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags);
+ public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags);
/**
* Called when layout of the windows is about to start.
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index bcd7bae..8eeed3d 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -51,11 +51,11 @@ LOCAL_SRC_FILES:= \
android_view_InputChannel.cpp \
android_view_InputQueue.cpp \
android_view_KeyEvent.cpp \
+ android_view_KeyCharacterMap.cpp \
android_view_GLES20Canvas.cpp \
android_view_MotionEvent.cpp \
android_text_AndroidCharacter.cpp \
android_text_AndroidBidi.cpp \
- android_text_KeyCharacterMap.cpp \
android_os_Debug.cpp \
android_os_FileUtils.cpp \
android_os_MemoryFile.cpp \
diff --git a/core/jni/android_text_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index a7e62c1..bfeec4f 100644
--- a/core/jni/android_text_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -29,6 +29,14 @@ static struct {
jclass clazz;
} gKeyEventClassInfo;
+static struct {
+ jclass clazz;
+
+ jfieldID keyCode;
+ jfieldID metaState;
+} gFallbackActionClassInfo;
+
+
static jint nativeLoad(JNIEnv *env, jobject clazz, jint deviceId) {
KeyCharacterMap* map;
status_t status = KeyCharacterMap::loadByDeviceId(deviceId, &map);
@@ -54,6 +62,21 @@ static jchar nativeGetCharacter(JNIEnv *env, jobject clazz, jint ptr,
return map->getCharacter(keyCode, metaState);
}
+static jboolean nativeGetFallbackAction(JNIEnv *env, jobject clazz, jint ptr, jint keyCode,
+ jint metaState, jobject fallbackActionObj) {
+ KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
+ KeyCharacterMap::FallbackAction fallbackAction;
+
+ bool result = map->getFallbackAction(keyCode, metaState, &fallbackAction);
+ if (result) {
+ env->SetIntField(fallbackActionObj, gFallbackActionClassInfo.keyCode,
+ fallbackAction.keyCode);
+ env->SetIntField(fallbackActionObj, gFallbackActionClassInfo.metaState,
+ fallbackAction.metaState);
+ }
+ return result;
+}
+
static jchar nativeGetNumber(JNIEnv *env, jobject clazz, jint ptr, jint keyCode) {
KeyCharacterMap* map = reinterpret_cast<KeyCharacterMap*>(ptr);
return map->getNumber(keyCode);
@@ -126,6 +149,8 @@ static JNINativeMethod g_methods[] = {
(void*)nativeDispose },
{ "nativeGetCharacter", "(III)C",
(void*)nativeGetCharacter },
+ { "nativeGetFallbackAction", "(IIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
+ (void*)nativeGetFallbackAction },
{ "nativeGetNumber", "(II)C",
(void*)nativeGetNumber },
{ "nativeGetMatch", "(II[CI)C",
@@ -143,10 +168,22 @@ static JNINativeMethod g_methods[] = {
LOG_FATAL_IF(! var, "Unable to find class " className); \
var = jclass(env->NewGlobalRef(var));
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
int register_android_text_KeyCharacterMap(JNIEnv* env)
{
FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
+ FIND_CLASS(gFallbackActionClassInfo.clazz, "android/view/KeyCharacterMap$FallbackAction");
+
+ GET_FIELD_ID(gFallbackActionClassInfo.keyCode, gFallbackActionClassInfo.clazz,
+ "keyCode", "I");
+
+ GET_FIELD_ID(gFallbackActionClassInfo.metaState, gFallbackActionClassInfo.clazz,
+ "metaState", "I");
+
return AndroidRuntime::registerNativeMethods(env,
"android/view/KeyCharacterMap", g_methods, NELEM(g_methods));
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 81191ff..4bd91b3 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1267,6 +1267,7 @@
<enum name="KEYCODE_PROG_GREEN" value="184" />
<enum name="KEYCODE_PROG_YELLOW" value="185" />
<enum name="KEYCODE_PROG_BLUE" value="186" />
+ <enum name="KEYCODE_APP_SWITCH" value="187" />
</attr>
<!-- ***************************************************************** -->
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 24b485d..14d7c80 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -42,7 +42,9 @@ key C {
label: 'C'
base: 'c'
shift, capslock: 'C'
- ctrl, alt, meta: none
+ alt: '\u00e7'
+ shift+alt: '\u00c7'
+ ctrl, meta: none
}
key D {
@@ -56,7 +58,8 @@ key E {
label: 'E'
base: 'e'
shift, capslock: 'E'
- ctrl, alt, meta: none
+ alt: '\u0301'
+ ctrl, meta: none
}
key F {
@@ -84,7 +87,8 @@ key I {
label: 'I'
base: 'i'
shift, capslock: 'I'
- ctrl, alt, meta: none
+ alt: '\u0302'
+ ctrl, meta: none
}
key J {
@@ -119,7 +123,8 @@ key N {
label: 'N'
base: 'n'
shift, capslock: 'N'
- ctrl, alt, meta: none
+ alt: '\u0303'
+ ctrl, meta: none
}
key O {
@@ -154,7 +159,8 @@ key S {
label: 'S'
base: 's'
shift, capslock: 'S'
- ctrl, alt, meta: none
+ alt: '\u00df'
+ ctrl, meta: none
}
key T {
@@ -168,7 +174,8 @@ key U {
label: 'U'
base: 'u'
shift, capslock: 'U'
- ctrl, alt, meta: none
+ alt: '\u0308'
+ ctrl, meta: none
}
key V {
@@ -253,6 +260,7 @@ key 6 {
base: '6'
shift: '^'
ctrl, alt, meta: none
+ alt+shift: '\u0302'
}
key 7 {
@@ -279,7 +287,8 @@ key 9 {
key SPACE {
label: ' '
base: ' '
- ctrl, alt, meta: none
+ ctrl, alt: none
+ meta: fallback SEARCH
}
key ENTER {
@@ -291,7 +300,8 @@ key ENTER {
key TAB {
label: '\t'
base: '\t'
- ctrl, alt, meta: none
+ ctrl, alt: none
+ meta: fallback APP_SWITCH
}
key COMMA {
@@ -319,7 +329,9 @@ key GRAVE {
label, number: '`'
base: '`'
shift: '~'
- ctrl, alt, meta: none
+ alt: '\u0300'
+ alt+shift: '\u0303'
+ ctrl, meta: none
}
key MINUS {
@@ -525,3 +537,11 @@ key PLUS {
label, number: '+'
base: '+'
}
+
+### Non-printing keys ###
+
+key ESCAPE {
+ base: fallback BACK
+ meta: fallback HOME
+ alt: fallback MENU
+}
diff --git a/data/keyboards/Vendor_05ac_Product_0239.kl b/data/keyboards/Vendor_05ac_Product_0239.kl
index 5234d58..6bd3753 100644
--- a/data/keyboards/Vendor_05ac_Product_0239.kl
+++ b/data/keyboards/Vendor_05ac_Product_0239.kl
@@ -104,7 +104,7 @@ key 111 FORWARD_DEL
key 113 VOLUME_MUTE
key 114 VOLUME_DOWN
key 115 VOLUME_UP
-# key 120 switch applications
+key 120 APP_SWITCH
key 125 META_LEFT
key 126 META_RIGHT
key 161 MEDIA_EJECT
diff --git a/data/keyboards/Vendor_22b8_Product_093d.kl b/data/keyboards/Vendor_22b8_Product_093d.kl
index 87b3c32..2749c5b 100644
--- a/data/keyboards/Vendor_22b8_Product_093d.kl
+++ b/data/keyboards/Vendor_22b8_Product_093d.kl
@@ -12,6 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+#
+# Motorola Bluetooth Wireless Keyboard.
+#
+
key 1 BACK
key 2 1
key 3 2
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
index 8d3c7ac..0ce4a68 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -39,7 +39,9 @@ key C {
label: 'C'
base: 'c'
shift, capslock: 'C'
- ctrl, alt, meta: none
+ alt: '\u00e7'
+ shift+alt: '\u00c7'
+ ctrl, meta: none
}
key D {
@@ -53,7 +55,8 @@ key E {
label: 'E'
base: 'e'
shift, capslock: 'E'
- ctrl, alt, meta: none
+ alt: '\u0301'
+ ctrl, meta: none
}
key F {
@@ -81,7 +84,8 @@ key I {
label: 'I'
base: 'i'
shift, capslock: 'I'
- ctrl, alt, meta: none
+ alt: '\u0302'
+ ctrl, meta: none
}
key J {
@@ -116,7 +120,8 @@ key N {
label: 'N'
base: 'n'
shift, capslock: 'N'
- ctrl, alt, meta: none
+ alt: '\u0303'
+ ctrl, meta: none
}
key O {
@@ -151,7 +156,8 @@ key S {
label: 'S'
base: 's'
shift, capslock: 'S'
- ctrl, alt, meta: none
+ alt: '\u00df'
+ ctrl, meta: none
}
key T {
@@ -165,7 +171,8 @@ key U {
label: 'U'
base: 'u'
shift, capslock: 'U'
- ctrl, alt, meta: none
+ alt: '\u0308'
+ ctrl, meta: none
}
key V {
@@ -250,6 +257,7 @@ key 6 {
base: '6'
shift: '^'
ctrl, alt, meta: none
+ alt+shift: '\u0302'
}
key 7 {
@@ -276,7 +284,8 @@ key 9 {
key SPACE {
label: ' '
base: ' '
- ctrl, alt, meta: none
+ ctrl, alt: none
+ meta: fallback SEARCH
}
key ENTER {
@@ -288,7 +297,8 @@ key ENTER {
key TAB {
label: '\t'
base: '\t'
- ctrl, alt, meta: none
+ ctrl, alt: none
+ meta: fallback APP_SWITCH
}
key COMMA {
@@ -316,7 +326,9 @@ key GRAVE {
label, number: '`'
base: '`'
shift: '~'
- ctrl, alt, meta: none
+ alt: '\u0300'
+ alt+shift: '\u0303'
+ ctrl, meta: none
}
key MINUS {
@@ -522,3 +534,11 @@ key PLUS {
label, number: '+'
base: '+'
}
+
+### Non-printing keys ###
+
+key ESCAPE {
+ base: fallback BACK
+ meta: fallback HOME
+ alt: fallback MENU
+}
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index b621680..7305601 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -306,9 +306,10 @@ public:
virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
- /* Allows the policy a chance to perform default processing for an unhandled key. */
+ /* Allows the policy a chance to perform default processing for an unhandled key.
+ * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */
virtual bool dispatchUnhandledKey(const sp<InputChannel>& inputChannel,
- const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
+ const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0;
/* Notifies the policy about switch events.
*/
@@ -735,6 +736,7 @@ private:
CANCEL_ALL_EVENTS = 0,
CANCEL_POINTER_EVENTS = 1,
CANCEL_NON_POINTER_EVENTS = 2,
+ CANCEL_FALLBACK_EVENTS = 3,
};
InputState();
@@ -771,6 +773,7 @@ private:
int32_t source;
int32_t keyCode;
int32_t scanCode;
+ int32_t flags;
nsecs_t downTime;
};
@@ -790,7 +793,10 @@ private:
Vector<KeyMemento> mKeyMementos;
Vector<MotionMemento> mMotionMementos;
- static bool shouldCancelEvent(int32_t eventSource, CancelationOptions options);
+ static bool shouldCancelKey(const KeyMemento& memento,
+ CancelationOptions options);
+ static bool shouldCancelMotion(const MotionMemento& memento,
+ CancelationOptions options);
};
/* Manages the dispatch state associated with a single input channel. */
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 8ec5421..b466ff1 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -402,7 +402,6 @@ private:
} mLocked;
void initializeLocked();
- void initializeLedStateLocked(LockedState::LedState& ledState, int32_t led);
void configureParameters();
void dumpParameters(String8& dump);
@@ -414,6 +413,8 @@ private:
ssize_t findKeyDownLocked(int32_t scanCode);
+ void resetLedStateLocked();
+ void initializeLedStateLocked(LockedState::LedState& ledState, int32_t led);
void updateLedStateLocked(bool reset);
void updateLedStateForModifierLocked(LockedState::LedState& ledState, int32_t led,
int32_t modifier, bool reset);
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index 7efb6cc..119db81 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -256,7 +256,7 @@ public:
* Returns WOULD_BLOCK if there is no signal present.
* Other errors probably indicate that the channel is broken.
*/
- status_t receiveFinishedSignal(bool& outHandled);
+ status_t receiveFinishedSignal(bool* outHandled);
private:
sp<InputChannel> mChannel;
diff --git a/include/ui/KeyCharacterMap.h b/include/ui/KeyCharacterMap.h
index a1ccb37..10a3810 100644
--- a/include/ui/KeyCharacterMap.h
+++ b/include/ui/KeyCharacterMap.h
@@ -44,6 +44,12 @@ public:
KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
};
+ // Substitute key code and meta state for fallback action.
+ struct FallbackAction {
+ int32_t keyCode;
+ int32_t metaState;
+ };
+
~KeyCharacterMap();
static status_t load(const String8& filename, KeyCharacterMap** outMap);
@@ -67,6 +73,13 @@ public:
*/
char16_t getCharacter(int32_t keyCode, int32_t metaState) const;
+ /* Gets the fallback action to use by default if the application does not
+ * handle the specified key.
+ * Returns true if an action was available, false if none.
+ */
+ bool getFallbackAction(int32_t keyCode, int32_t metaState,
+ FallbackAction* outFallbackAction) 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.
@@ -155,6 +168,10 @@ private:
KeyCharacterMap();
+ bool getKey(int32_t keyCode, const Key** outKey) const;
+ bool getKeyBehavior(int32_t keyCode, int32_t metaState,
+ const Key** outKey, const Behavior** outBehavior) const;
+
bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
static void addKey(Vector<KeyEvent>& outEvents,
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index be7db1f..9b1a897 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -211,6 +211,7 @@ static const KeycodeLabel KEYCODES[] = {
{ "PROG_GREEN", 184 },
{ "PROG_YELLOW", 185 },
{ "PROG_BLUE", 186 },
+ { "APP_SWITCH", 187 },
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index f1223f1..1f6a920 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -1884,7 +1884,7 @@ int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data
}
bool handled = false;
- status_t status = connection->inputPublisher.receiveFinishedSignal(handled);
+ status_t status = connection->inputPublisher.receiveFinishedSignal(&handled);
if (status) {
LOGE("channel '%s' ~ Failed to receive finished signal. status=%d",
connection->getInputChannelName(), status);
@@ -3039,21 +3039,57 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
sp<Connection> connection = commandEntry->connection;
bool handled = commandEntry->handled;
- if (!handled && !connection->outboundQueue.isEmpty()) {
+ if (!connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
if (dispatchEntry->inProgress
&& dispatchEntry->hasForegroundTarget()
&& dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
- KeyEvent event;
- initializeKeyEvent(&event, keyEntry);
-
- mLock.unlock();
-
- mPolicy->dispatchUnhandledKey(connection->inputChannel,
- &event, keyEntry->policyFlags);
-
- mLock.lock();
+ if (!(keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK)) {
+ if (handled) {
+ // If the application handled a non-fallback key, then immediately
+ // cancel all fallback keys previously dispatched to the application.
+ // This behavior will prevent chording with fallback keys (so they cannot
+ // be used as modifiers) but it will ensure that fallback keys do not
+ // get stuck. This takes care of the case where the application does not handle
+ // the original DOWN so we generate a fallback DOWN but it does handle
+ // the original UP in which case we would not generate the fallback UP.
+ synthesizeCancelationEventsForConnectionLocked(connection,
+ InputState::CANCEL_FALLBACK_EVENTS,
+ "Application handled a non-fallback event.");
+ } else {
+ // If the application did not handle a non-fallback key, then ask
+ // the policy what to do with it. We might generate a fallback key
+ // event here.
+ KeyEvent event;
+ initializeKeyEvent(&event, keyEntry);
+
+ mLock.unlock();
+
+ bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel,
+ &event, keyEntry->policyFlags, &event);
+
+ mLock.lock();
+
+ if (fallback) {
+ // Restart the dispatch cycle using the fallback key.
+ keyEntry->eventTime = event.getEventTime();
+ keyEntry->deviceId = event.getDeviceId();
+ keyEntry->source = event.getSource();
+ keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
+ keyEntry->keyCode = event.getKeyCode();
+ keyEntry->scanCode = event.getScanCode();
+ keyEntry->metaState = event.getMetaState();
+ keyEntry->repeatCount = event.getRepeatCount();
+ keyEntry->downTime = event.getDownTime();
+ keyEntry->syntheticRepeat = false;
+
+ dispatchEntry->inProgress = false;
+ startDispatchCycleLocked(now(), connection);
+ return;
+ }
+ }
+ }
}
}
@@ -3371,6 +3407,7 @@ InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey(
memento.source = entry->source;
memento.keyCode = entry->keyCode;
memento.scanCode = entry->scanCode;
+ memento.flags = entry->flags;
memento.downTime = entry->downTime;
return CONSISTENT;
}
@@ -3453,10 +3490,10 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim
CancelationOptions options) {
for (size_t i = 0; i < mKeyMementos.size(); ) {
const KeyMemento& memento = mKeyMementos.itemAt(i);
- if (shouldCancelEvent(memento.source, options)) {
+ if (shouldCancelKey(memento, options)) {
outEvents.push(allocator->obtainKeyEntry(currentTime,
memento.deviceId, memento.source, 0,
- AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED,
+ AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED,
memento.keyCode, memento.scanCode, 0, 0, memento.downTime));
mKeyMementos.removeAt(i);
} else {
@@ -3466,7 +3503,7 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim
for (size_t i = 0; i < mMotionMementos.size(); ) {
const MotionMemento& memento = mMotionMementos.itemAt(i);
- if (shouldCancelEvent(memento.source, options)) {
+ if (shouldCancelMotion(memento, options)) {
outEvents.push(allocator->obtainMotionEntry(currentTime,
memento.deviceId, memento.source, 0,
AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0,
@@ -3502,15 +3539,30 @@ void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
}
}
-bool InputDispatcher::InputState::shouldCancelEvent(int32_t eventSource,
+bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento,
CancelationOptions options) {
switch (options) {
- case CANCEL_POINTER_EVENTS:
- return eventSource & AINPUT_SOURCE_CLASS_POINTER;
+ case CANCEL_ALL_EVENTS:
case CANCEL_NON_POINTER_EVENTS:
- return !(eventSource & AINPUT_SOURCE_CLASS_POINTER);
+ return true;
+ case CANCEL_FALLBACK_EVENTS:
+ return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
default:
+ return false;
+ }
+}
+
+bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento,
+ CancelationOptions options) {
+ switch (options) {
+ case CANCEL_ALL_EVENTS:
return true;
+ case CANCEL_POINTER_EVENTS:
+ return memento.source & AINPUT_SOURCE_CLASS_POINTER;
+ case CANCEL_NON_POINTER_EVENTS:
+ return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
+ default:
+ return false;
}
}
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 9cc96ad..51ed09f 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -745,17 +745,6 @@ KeyboardInputMapper::~KeyboardInputMapper() {
void KeyboardInputMapper::initializeLocked() {
mLocked.metaState = AMETA_NONE;
mLocked.downTime = 0;
-
- initializeLedStateLocked(mLocked.capsLockLedState, LED_CAPSL);
- initializeLedStateLocked(mLocked.numLockLedState, LED_NUML);
- initializeLedStateLocked(mLocked.scrollLockLedState, LED_SCROLLL);
-
- updateLedStateLocked(true);
-}
-
-void KeyboardInputMapper::initializeLedStateLocked(LockedState::LedState& ledState, int32_t led) {
- ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
- ledState.on = false;
}
uint32_t KeyboardInputMapper::getSources() {
@@ -786,6 +775,12 @@ void KeyboardInputMapper::configure() {
// Configure basic parameters.
configureParameters();
+
+ // Reset LEDs.
+ {
+ AutoMutex _l(mLock);
+ resetLedStateLocked();
+ }
}
void KeyboardInputMapper::configureParameters() {
@@ -813,6 +808,7 @@ void KeyboardInputMapper::reset() {
// Synthesize key up event on reset if keys are currently down.
if (mLocked.keyDowns.isEmpty()) {
initializeLocked();
+ resetLedStateLocked();
break; // done
}
@@ -953,6 +949,19 @@ int32_t KeyboardInputMapper::getMetaState() {
} // release lock
}
+void KeyboardInputMapper::resetLedStateLocked() {
+ initializeLedStateLocked(mLocked.capsLockLedState, LED_CAPSL);
+ initializeLedStateLocked(mLocked.numLockLedState, LED_NUML);
+ initializeLedStateLocked(mLocked.scrollLockLedState, LED_SCROLLL);
+
+ updateLedStateLocked(true);
+}
+
+void KeyboardInputMapper::initializeLedStateLocked(LockedState::LedState& ledState, int32_t led) {
+ ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
+ ledState.on = false;
+}
+
void KeyboardInputMapper::updateLedStateLocked(bool reset) {
updateLedStateForModifierLocked(mLocked.capsLockLedState, LED_CAPSL,
AMETA_CAPS_LOCK_ON, reset);
@@ -966,7 +975,7 @@ void KeyboardInputMapper::updateLedStateForModifierLocked(LockedState::LedState&
int32_t led, int32_t modifier, bool reset) {
if (ledState.avail) {
bool desiredState = (mLocked.metaState & modifier) != 0;
- if (ledState.on != desiredState) {
+ if (reset || ledState.on != desiredState) {
getEventHub()->setLedState(getDeviceId(), led, desiredState);
ledState.on = desiredState;
}
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index 1885691..83d9556 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -501,7 +501,7 @@ status_t InputPublisher::sendDispatchSignal() {
return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
}
-status_t InputPublisher::receiveFinishedSignal(bool& outHandled) {
+status_t InputPublisher::receiveFinishedSignal(bool* outHandled) {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ receiveFinishedSignal",
mChannel->getName().string());
@@ -510,13 +510,13 @@ status_t InputPublisher::receiveFinishedSignal(bool& outHandled) {
char signal;
status_t result = mChannel->receiveSignal(& signal);
if (result) {
- outHandled = false;
+ *outHandled = false;
return result;
}
if (signal == INPUT_SIGNAL_FINISHED_HANDLED) {
- outHandled = true;
+ *outHandled = true;
} else if (signal == INPUT_SIGNAL_FINISHED_UNHANDLED) {
- outHandled = false;
+ *outHandled = false;
} else {
LOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer",
mChannel->getName().string(), signal);
diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp
index e689c4b..9bfa8f6 100644
--- a/libs/ui/KeyCharacterMap.cpp
+++ b/libs/ui/KeyCharacterMap.cpp
@@ -141,9 +141,8 @@ int32_t KeyCharacterMap::getKeyboardType() const {
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);
+ const Key* key;
+ if (getKey(keyCode, &key)) {
result = key->label;
}
#if DEBUG_MAPPING
@@ -154,9 +153,8 @@ char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
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);
+ const Key* key;
+ if (getKey(keyCode, &key)) {
result = key->number;
}
#if DEBUG_MAPPING
@@ -167,15 +165,10 @@ char16_t KeyCharacterMap::getNumber(int32_t keyCode) const {
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;
- }
- }
+ const Key* key;
+ const Behavior* behavior;
+ if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
+ result = behavior->character;
}
#if DEBUG_MAPPING
LOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result);
@@ -183,13 +176,33 @@ char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const
return result;
}
+bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState,
+ FallbackAction* outFallbackAction) const {
+ outFallbackAction->keyCode = 0;
+ outFallbackAction->metaState = 0;
+
+ bool result = false;
+ const Key* key;
+ const Behavior* behavior;
+ if (getKeyBehavior(keyCode, metaState, &key, &behavior)) {
+ outFallbackAction->keyCode = behavior->fallbackKeyCode;
+ outFallbackAction->metaState = metaState & ~behavior->metaState;
+ result = true;
+ }
+#if DEBUG_MAPPING
+ LOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, "
+ "fallback keyCode=%d, fallback metaState=0x%08x.",
+ keyCode, metaState, result ? "true" : "false",
+ outFallbackAction->keyCode, outFallbackAction->metaState);
+#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);
-
+ const Key* key;
+ if (getKey(keyCode, &key)) {
// 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.
@@ -238,7 +251,7 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t
}
#if DEBUG_MAPPING
LOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.",
- deviceId, toString(chars, numChars).string(), outEvents.size());
+ deviceId, toString(chars, numChars).string(), int32_t(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(),
@@ -248,6 +261,32 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t
return true;
}
+bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
+ ssize_t index = mKeys.indexOfKey(keyCode);
+ if (index >= 0) {
+ *outKey = mKeys.valueAt(index);
+ return true;
+ }
+ return false;
+}
+
+bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState,
+ const Key** outKey, const Behavior** outBehavior) const {
+ const Key* key;
+ if (getKey(keyCode, &key)) {
+ const Behavior* behavior = key->firstBehavior;
+ while (behavior) {
+ if ((behavior->metaState & metaState) == behavior->metaState) {
+ *outKey = key;
+ *outBehavior = behavior;
+ return true;
+ }
+ behavior = behavior->next;
+ }
+ }
+ return false;
+}
+
bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const {
if (!ch) {
return false;
diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp
index 68f9037..7e17c57 100644
--- a/libs/ui/tests/InputDispatcher_test.cpp
+++ b/libs/ui/tests/InputDispatcher_test.cpp
@@ -66,7 +66,7 @@ private:
}
virtual bool dispatchUnhandledKey(const sp<InputChannel>& inputChannel,
- const KeyEvent* keyEvent, uint32_t policyFlags) {
+ const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) {
return false;
}
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
index c6eac25..903fcaf 100644
--- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -123,7 +123,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
<< "consumer sendFinishedSignal should return OK";
bool handled = false;
- status = mPublisher->receiveFinishedSignal(handled);
+ status = mPublisher->receiveFinishedSignal(&handled);
ASSERT_EQ(OK, status)
<< "publisher receiveFinishedSignal should return OK";
ASSERT_TRUE(handled)
@@ -287,7 +287,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
<< "consumer sendFinishedSignal should return OK";
bool handled = true;
- status = mPublisher->receiveFinishedSignal(handled);
+ status = mPublisher->receiveFinishedSignal(&handled);
ASSERT_EQ(OK, status)
<< "publisher receiveFinishedSignal should return OK";
ASSERT_FALSE(handled)
diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp
index d6c2cbd..97cbc25 100644
--- a/libs/ui/tests/InputReader_test.cpp
+++ b/libs/ui/tests/InputReader_test.cpp
@@ -1137,6 +1137,7 @@ protected:
mFakeDispatcher = new FakeInputDispatcher();
mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeDispatcher);
+ mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME));
}
@@ -1753,7 +1754,7 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds)
process(mapper, ARBITRARY_TIME, DEVICE_ID,
EV_KEY, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 1, 0);
process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 1, 0);
+ EV_KEY, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 0, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
@@ -2225,19 +2226,19 @@ void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
}
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenDisplayTypeIsTouchPad_ReturnsTouchPad) {
+TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchPad_ReturnsTouchPad) {
SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareAxes(POSITION);
- addConfigurationProperty("touch.displayType", "touchPad");
+ addConfigurationProperty("touch.deviceType", "touchPad");
addMapperAndConfigure(mapper);
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
}
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenDisplayTypeIsTouchScreen_ReturnsTouchScreen) {
+TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) {
SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
prepareAxes(POSITION);
- addConfigurationProperty("touch.displayType", "touchScreen");
+ addConfigurationProperty("touch.deviceType", "touchScreen");
addMapperAndConfigure(mapper);
ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
diff --git a/native/include/android/input.h b/native/include/android/input.h
index 861ec8b..e196686 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -215,7 +215,15 @@ enum {
* tracked from its initial down. That is, somebody requested that tracking
* started on the key down and a long press has not caused
* the tracking to be canceled. */
- AKEY_EVENT_FLAG_TRACKING = 0x200
+ AKEY_EVENT_FLAG_TRACKING = 0x200,
+
+ /* Set when a key event has been synthesized to implement default behavior
+ * for an event that the application did not handle.
+ * Fallback key events are generated by unhandled trackball motions
+ * (to emulate a directional keypad) and by certain unhandled key presses
+ * that are declared in the key map (such as special function numeric keypad
+ * keys when numlock is off). */
+ AKEY_EVENT_FLAG_FALLBACK = 0x400,
};
/*
diff --git a/native/include/android/keycodes.h b/native/include/android/keycodes.h
index 5f33ad5..b026a0c 100644
--- a/native/include/android/keycodes.h
+++ b/native/include/android/keycodes.h
@@ -230,6 +230,7 @@ enum {
AKEYCODE_PROG_GREEN = 184,
AKEYCODE_PROG_YELLOW = 185,
AKEYCODE_PROG_BLUE = 186,
+ AKEYCODE_APP_SWITCH = 187,
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index c3112d8..e61dd6c 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -133,6 +133,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final boolean DEBUG = false;
static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
static final boolean DEBUG_LAYOUT = false;
+ static final boolean DEBUG_FALLBACK = false;
static final boolean SHOW_STARTING_ANIMATIONS = true;
static final boolean SHOW_PROCESSES_ON_ALT_MENU = false;
@@ -315,8 +316,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
- boolean mSearchKeyPressed;
- boolean mConsumeSearchKeyUp;
+ int mShortcutKeyPressed = -1;
+ boolean mConsumeShortcutKeyUp;
boolean mShowMenuKey = false; // track FLAG_NEEDS_MENU_KEY on frontmost window
// support for activating the lock screen while the screen is on
@@ -345,6 +346,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
ShortcutManager mShortcutManager;
PowerManager.WakeLock mBroadcastWakeLock;
+ final KeyCharacterMap.FallbackAction mFallbackAction = new KeyCharacterMap.FallbackAction();
+
class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
@@ -1228,56 +1231,44 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed);
}
- // Clear a pending HOME longpress if the user releases Home
- // TODO: This could probably be inside the next bit of logic, but that code
- // turned out to be a bit fragile so I'm doing it here explicitly, for now.
- if ((keyCode == KeyEvent.KEYCODE_HOME) && !down) {
- mHandler.removeCallbacks(mHomeLongPress);
- }
+ // First we always handle the home key here, so applications
+ // can never break it, although if keyguard is on, we do let
+ // it handle it, because that gives us the correct 5 second
+ // timeout.
+ if (keyCode == KeyEvent.KEYCODE_HOME) {
+ // Clear a pending HOME longpress if the user releases Home
+ if (!down) {
+ mHandler.removeCallbacks(mHomeLongPress);
+ }
- // If the HOME button is currently being held, then we do special
- // chording with it.
- if (mHomePressed) {
-
// If we have released the home key, and didn't do anything else
// while it was pressed, then it is time to go home!
- if (keyCode == KeyEvent.KEYCODE_HOME) {
- if (!down) {
- mHomePressed = false;
-
- if (!canceled) {
- // If an incoming call is ringing, HOME is totally disabled.
- // (The user is already on the InCallScreen at this point,
- // and his ONLY options are to answer or reject the call.)
- boolean incomingRinging = false;
- try {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- incomingRinging = telephonyService.isRinging();
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
- }
-
- if (incomingRinging) {
- Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
- } else {
- launchHomeFromHotKey();
+ if (mHomePressed && !down) {
+ mHomePressed = false;
+ if (!canceled) {
+ // If an incoming call is ringing, HOME is totally disabled.
+ // (The user is already on the InCallScreen at this point,
+ // and his ONLY options are to answer or reject the call.)
+ boolean incomingRinging = false;
+ try {
+ ITelephony telephonyService = getTelephonyService();
+ if (telephonyService != null) {
+ incomingRinging = telephonyService.isRinging();
}
+ } catch (RemoteException ex) {
+ Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
+ }
+
+ if (incomingRinging) {
+ Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
} else {
- Log.i(TAG, "Ignoring HOME; event canceled.");
+ launchHomeFromHotKey();
}
+ } else {
+ Log.i(TAG, "Ignoring HOME; event canceled.");
}
+ return true;
}
-
- return true;
- }
-
- // First we always handle the home key here, so applications
- // can never break it, although if keyguard is on, we do let
- // it handle it, because that gives us the correct 5 second
- // timeout.
- if (keyCode == KeyEvent.KEYCODE_HOME) {
// If a system window has focus, then it doesn't make sense
// right now to interact with applications.
@@ -1334,21 +1325,28 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
if (down) {
if (repeatCount == 0) {
- mSearchKeyPressed = true;
+ mShortcutKeyPressed = keyCode;
+ mConsumeShortcutKeyUp = false;
}
- } else {
- mSearchKeyPressed = false;
+ } else if (keyCode == mShortcutKeyPressed) {
+ mShortcutKeyPressed = -1;
- if (mConsumeSearchKeyUp) {
+ if (mConsumeShortcutKeyUp) {
// Consume the up-event
- mConsumeSearchKeyUp = false;
+ mConsumeShortcutKeyUp = false;
return true;
}
}
+ } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
+ if (!down) {
+ showRecentAppsDialog();
+ }
+ return true;
}
- // Shortcuts are invoked through Search+key, so intercept those here
- if (mSearchKeyPressed) {
+ // Shortcuts are invoked through Search+key or Meta+key, so intercept those here
+ if ((mShortcutKeyPressed != -1 && !mConsumeShortcutKeyUp)
+ || (metaState & KeyEvent.META_META_ON) != 0) {
if (down && repeatCount == 0 && !keyguardOn) {
Intent shortcutIntent = mShortcutManager.getIntent(event);
if (shortcutIntent != null) {
@@ -1359,7 +1357,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* We launched an app, so the up-event of the search key
* should be consumed
*/
- mConsumeSearchKeyUp = true;
+ if (mShortcutKeyPressed != -1) {
+ mConsumeShortcutKeyUp = true;
+ }
return true;
}
}
@@ -1370,8 +1370,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** {@inheritDoc} */
@Override
- public boolean dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
- if (false) {
+ public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
+ if (DEBUG_FALLBACK) {
Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + event.getAction()
+ ", flags=" + event.getFlags()
+ ", keyCode=" + event.getKeyCode()
@@ -1380,7 +1380,42 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ ", repeatCount=" + event.getRepeatCount()
+ ", policyFlags=" + policyFlags);
}
- return false;
+
+ if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
+ final KeyCharacterMap kcm = event.getKeyCharacterMap();
+ boolean fallback = kcm.getFallbackAction(event.getKeyCode(), event.getMetaState(),
+ mFallbackAction);
+
+ if (fallback) {
+ if (DEBUG_FALLBACK) {
+ Slog.d(TAG, "Fallback: keyCode=" + mFallbackAction.keyCode
+ + " metaState=" + Integer.toHexString(mFallbackAction.metaState));
+ }
+
+ int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
+ KeyEvent fallbackEvent = KeyEvent.obtain(
+ event.getDownTime(), event.getEventTime(),
+ event.getAction(), mFallbackAction.keyCode,
+ event.getRepeatCount(), mFallbackAction.metaState,
+ event.getDeviceId(), event.getScanCode(),
+ flags, event.getSource(), null);
+ int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags, true);
+ if ((actions & ACTION_PASS_TO_USER) != 0) {
+ if (!interceptKeyBeforeDispatching(win, fallbackEvent, policyFlags)) {
+ if (DEBUG_FALLBACK) {
+ Slog.d(TAG, "Performing fallback.");
+ }
+ return fallbackEvent;
+ }
+ }
+ fallbackEvent.recycle();
+ }
+ }
+
+ if (DEBUG_FALLBACK) {
+ Slog.d(TAG, "No fallback.");
+ }
+ return null;
}
/**
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 8634eec..4c499cd 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -402,7 +402,7 @@ public class InputManager {
}
@SuppressWarnings("unused")
- public boolean dispatchUnhandledKey(InputChannel focus,
+ public KeyEvent dispatchUnhandledKey(InputChannel focus,
KeyEvent event, int policyFlags) {
return mWindowManagerService.mInputMonitor.dispatchUnhandledKey(
focus, event, policyFlags);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 2d88b2f..f78ebb9 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5844,7 +5844,7 @@ public class WindowManagerService extends IWindowManager.Stub
/* Provides an opportunity for the window manager policy to process a key that
* the application did not handle. */
- public boolean dispatchUnhandledKey(
+ public KeyEvent dispatchUnhandledKey(
InputChannel focus, KeyEvent event, int policyFlags) {
WindowState windowState = getWindowStateForInputChannel(focus);
return mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 1996dd0..9156249 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -183,7 +183,7 @@ public:
virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags);
virtual bool dispatchUnhandledKey(const sp<InputChannel>& inputChannel,
- const KeyEvent* keyEvent, uint32_t policyFlags);
+ const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent);
virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
virtual bool checkInjectEventsPermissionNonReentrant(
int32_t injectorPid, int32_t injectorUid);
@@ -813,7 +813,7 @@ bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& i
// - Ignore untrusted events and pass them along.
// - Filter normal events and trusted injected events through the window manager policy to
// handle the HOME key and the like.
- bool result;
+ bool result = false;
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
@@ -830,21 +830,17 @@ bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& i
result = consumed && !error;
} else {
LOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
- result = false;
}
-
env->DeleteLocalRef(inputChannelObj);
- } else {
- result = false;
}
return result;
}
bool NativeInputManager::dispatchUnhandledKey(const sp<InputChannel>& inputChannel,
- const KeyEvent* keyEvent, uint32_t policyFlags) {
+ const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) {
// Policy:
// - Ignore untrusted events and do not perform default handling.
- bool result;
+ bool result = false;
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
@@ -852,21 +848,26 @@ bool NativeInputManager::dispatchUnhandledKey(const sp<InputChannel>& inputChann
jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
if (keyEventObj) {
- jboolean handled = env->CallBooleanMethod(mCallbacksObj,
+ jobject fallbackKeyEventObj = env->CallObjectMethod(mCallbacksObj,
gCallbacksClassInfo.dispatchUnhandledKey,
inputChannelObj, keyEventObj, policyFlags);
- bool error = checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey");
+ checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey");
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
- result = handled && !error;
+
+ if (fallbackKeyEventObj) {
+ // Note: outFallbackKeyEvent may be the same object as keyEvent.
+ if (!android_view_KeyEvent_toNative(env, fallbackKeyEventObj,
+ outFallbackKeyEvent)) {
+ result = true;
+ }
+ android_view_KeyEvent_recycle(env, fallbackKeyEventObj);
+ env->DeleteLocalRef(fallbackKeyEventObj);
+ }
} else {
LOGE("Failed to obtain key event object for dispatchUnhandledKey.");
- result = false;
}
-
env->DeleteLocalRef(inputChannelObj);
- } else {
- result = false;
}
return result;
}
@@ -1307,7 +1308,8 @@ int register_android_server_InputManager(JNIEnv* env) {
"(Landroid/view/InputChannel;Landroid/view/KeyEvent;I)Z");
GET_METHOD_ID(gCallbacksClassInfo.dispatchUnhandledKey, gCallbacksClassInfo.clazz,
- "dispatchUnhandledKey", "(Landroid/view/InputChannel;Landroid/view/KeyEvent;I)Z");
+ "dispatchUnhandledKey",
+ "(Landroid/view/InputChannel;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
"checkInjectEventsPermission", "(II)Z");