diff options
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"); |