Bug 1112212 - Send key events in onKeyMultiple; r=cpeterson
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -258,19 +258,19 @@ public class GeckoEvent {
public static GeckoEvent createAppForegroundingEvent() {
return GeckoEvent.get(NativeGeckoEvent.APP_FOREGROUNDING);
}
public static GeckoEvent createNoOpEvent() {
return GeckoEvent.get(NativeGeckoEvent.NOOP);
}
- public static GeckoEvent createKeyEvent(KeyEvent k, int metaState) {
+ public static GeckoEvent createKeyEvent(KeyEvent k, int action, int metaState) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.KEY_EVENT);
- event.initKeyEvent(k, metaState);
+ event.initKeyEvent(k, action, metaState);
return event;
}
public static GeckoEvent createCompositorCreateEvent(int width, int height) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.COMPOSITOR_CREATE);
event.mWidth = width;
event.mHeight = height;
return event;
@@ -279,18 +279,21 @@ public class GeckoEvent {
public static GeckoEvent createCompositorPauseEvent() {
return GeckoEvent.get(NativeGeckoEvent.COMPOSITOR_PAUSE);
}
public static GeckoEvent createCompositorResumeEvent() {
return GeckoEvent.get(NativeGeckoEvent.COMPOSITOR_RESUME);
}
- private void initKeyEvent(KeyEvent k, int metaState) {
- mAction = k.getAction();
+ private void initKeyEvent(KeyEvent k, int action, int metaState) {
+ // Use a separate action argument so we can override the key's original action,
+ // e.g. change ACTION_MULTIPLE to ACTION_DOWN. That way we don't have to allocate
+ // a new key event just to change its action field.
+ mAction = action;
mTime = k.getEventTime();
// Normally we expect k.getMetaState() to reflect the current meta-state; however,
// some software-generated key events may not have k.getMetaState() set, e.g. key
// events from Swype. Therefore, it's necessary to combine the key's meta-states
// with the meta-states that we keep separately in KeyListener
mMetaState = k.getMetaState() | metaState;
mFlags = k.getFlags();
mKeyCode = k.getKeyCode();
@@ -615,17 +618,17 @@ public class GeckoEvent {
public static GeckoEvent createIMEEvent(ImeAction action) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.IME_EVENT);
event.mAction = action.value;
return event;
}
public static GeckoEvent createIMEKeyEvent(KeyEvent k) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.IME_KEY_EVENT);
- event.initKeyEvent(k, 0);
+ event.initKeyEvent(k, k.getAction(), 0);
return event;
}
public static GeckoEvent createIMEReplaceEvent(int start, int end, String text) {
return createIMETextEvent(false, start, end, text);
}
public static GeckoEvent createIMEComposeEvent(int start, int end, String text) {
--- a/mobile/android/base/GeckoInputConnection.java
+++ b/mobile/android/base/GeckoInputConnection.java
@@ -810,23 +810,24 @@ class GeckoInputConnection
event = GamepadUtils.translateSonyXperiaGamepadKeys(keyCode, event);
keyCode = event.getKeyCode();
}
if (keyCode > KeyEvent.getMaxKeyCode() ||
!shouldProcessKey(keyCode, event)) {
return false;
}
+ final int action = down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
event = translateKey(keyCode, event);
keyCode = event.getKeyCode();
View view = getView();
if (view == null) {
InputThreadUtils.sInstance.sendEventFromUiThread(ThreadUtils.getUiHandler(),
- mEditableClient, GeckoEvent.createKeyEvent(event, 0));
+ mEditableClient, GeckoEvent.createKeyEvent(event, action, 0));
return true;
}
// KeyListener returns true if it handled the event for us. KeyListener is only
// safe to use on the UI thread; therefore we need to pass a proxy Editable to it
KeyListener keyListener = TextKeyListener.getInstance();
Handler uiHandler = view.getRootView().getHandler();
Editable uiEditable = InputThreadUtils.sInstance.
@@ -834,17 +835,17 @@ class GeckoInputConnection
boolean skip = shouldSkipKeyListener(keyCode, event);
if (down) {
mEditableClient.setSuppressKeyUp(true);
}
if (skip ||
(down && !keyListener.onKeyDown(view, uiEditable, keyCode, event)) ||
(!down && !keyListener.onKeyUp(view, uiEditable, keyCode, event))) {
InputThreadUtils.sInstance.sendEventFromUiThread(uiHandler, mEditableClient,
- GeckoEvent.createKeyEvent(event, TextKeyListener.getMetaState(uiEditable)));
+ GeckoEvent.createKeyEvent(event, action, TextKeyListener.getMetaState(uiEditable)));
if (skip && down) {
// Usually, the down key listener call above adjusts meta states for us.
// However, if we skip that call above, we have to manually adjust meta
// states so the meta states remain consistent
TextKeyListener.adjustMetaAfterKeypress(uiEditable);
}
}
if (down) {
@@ -858,37 +859,53 @@ class GeckoInputConnection
return processKey(keyCode, event, true);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return processKey(keyCode, event, false);
}
+ /**
+ * Get a key that represents a given character.
+ */
+ private KeyEvent getCharKeyEvent(final char c) {
+ final long time = SystemClock.uptimeMillis();
+ return new KeyEvent(time, time, KeyEvent.ACTION_MULTIPLE,
+ KeyEvent.KEYCODE_UNKNOWN, /* repeat */ 0) {
+ @Override
+ public int getUnicodeChar() {
+ return c;
+ }
+
+ @Override
+ public int getUnicodeChar(int metaState) {
+ return c;
+ }
+ };
+ }
+
@Override
public boolean onKeyMultiple(int keyCode, int repeatCount, final KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
// KEYCODE_UNKNOWN means the characters are in KeyEvent.getCharacters()
- View view = getView();
- if (view != null) {
- InputThreadUtils.sInstance.runOnIcThread(
- view.getRootView().getHandler(), mEditableClient,
- new Runnable() {
- @Override public void run() {
- // Don't call GeckoInputConnection.commitText because it can
- // post a key event back to onKeyMultiple, causing a loop
- GeckoInputConnection.super.commitText(event.getCharacters(), 1);
- }
- });
+ final String str = event.getCharacters();
+ for (int i = 0; i < str.length(); i++) {
+ final KeyEvent charEvent = getCharKeyEvent(str.charAt(i));
+ if (!processKey(KeyEvent.KEYCODE_UNKNOWN, charEvent, /* down */ true) ||
+ !processKey(KeyEvent.KEYCODE_UNKNOWN, charEvent, /* down */ false)) {
+ return false;
+ }
}
return true;
}
+
while ((repeatCount--) != 0) {
- if (!processKey(keyCode, event, true) ||
- !processKey(keyCode, event, false)) {
+ if (!processKey(keyCode, event, /* down */ true) ||
+ !processKey(keyCode, event, /* down */ false)) {
return false;
}
}
return true;
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {