mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1248047 - Simplify GeckoInputConnection.processKey; r=esawin
Right now, processKey uses a IC-thread proxy in order to handle key events on the UI thread. This patch makes it post the key event to the IC thread and avoid the proxy entirely.
This commit is contained in:
parent
51ebebc9a5
commit
b501d5ca8c
@ -786,6 +786,11 @@ final class GeckoEditable extends JNIObject
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override // GeckoEditableClient
|
||||
public void postToInputConnection(final Runnable runnable) {
|
||||
mIcPostHandler.post(runnable);
|
||||
}
|
||||
|
||||
private void geckoSetIcHandler(final Handler newHandler) {
|
||||
geckoPostToIc(new Runnable() { // posting to old IC thread
|
||||
@Override
|
||||
|
@ -19,4 +19,5 @@ interface GeckoEditableClient {
|
||||
void setSuppressKeyUp(boolean suppress);
|
||||
Handler getInputConnectionHandler();
|
||||
boolean setInputConnectionHandler(Handler handler);
|
||||
void postToInputConnection(Runnable runnable);
|
||||
}
|
||||
|
@ -53,167 +53,6 @@ class GeckoInputConnection
|
||||
|
||||
private static Handler sBackgroundHandler;
|
||||
|
||||
private static class InputThreadUtils {
|
||||
// We only want one UI editable around to keep synchronization simple,
|
||||
// so we make InputThreadUtils a singleton
|
||||
public static final InputThreadUtils sInstance = new InputThreadUtils();
|
||||
|
||||
private Editable mUiEditable;
|
||||
private Object mUiEditableReturn;
|
||||
private Exception mUiEditableException;
|
||||
private final SynchronousQueue<Runnable> mIcRunnableSync;
|
||||
private final Runnable mIcSignalRunnable;
|
||||
|
||||
private InputThreadUtils() {
|
||||
mIcRunnableSync = new SynchronousQueue<Runnable>();
|
||||
mIcSignalRunnable = new Runnable() {
|
||||
@Override public void run() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void runOnIcThread(Handler icHandler, final Runnable runnable) {
|
||||
if (DEBUG) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
Log.d(LOGTAG, "runOnIcThread() on thread " +
|
||||
icHandler.getLooper().getThread().getName());
|
||||
}
|
||||
Runnable runner = new Runnable() {
|
||||
@Override public void run() {
|
||||
try {
|
||||
Runnable queuedRunnable = mIcRunnableSync.take();
|
||||
if (DEBUG && queuedRunnable != runnable) {
|
||||
throw new IllegalThreadStateException("sync error");
|
||||
}
|
||||
queuedRunnable.run();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
// if we are not inside waitForUiThread(), runner will call the runnable
|
||||
icHandler.post(runner);
|
||||
// runnable will be called by either runner from above or waitForUiThread()
|
||||
mIcRunnableSync.put(runnable);
|
||||
} catch (InterruptedException e) {
|
||||
} finally {
|
||||
// if waitForUiThread() already called runnable, runner should not call it again
|
||||
icHandler.removeCallbacks(runner);
|
||||
}
|
||||
}
|
||||
|
||||
public void endWaitForUiThread() {
|
||||
if (DEBUG) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
Log.d(LOGTAG, "endWaitForUiThread()");
|
||||
}
|
||||
try {
|
||||
mIcRunnableSync.put(mIcSignalRunnable);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void waitForUiThread(Handler icHandler) {
|
||||
if (DEBUG) {
|
||||
ThreadUtils.assertOnThread(icHandler.getLooper().getThread(), AssertBehavior.THROW);
|
||||
Log.d(LOGTAG, "waitForUiThread() blocking on thread " +
|
||||
icHandler.getLooper().getThread().getName());
|
||||
}
|
||||
try {
|
||||
Runnable runnable = null;
|
||||
do {
|
||||
runnable = mIcRunnableSync.take();
|
||||
runnable.run();
|
||||
} while (runnable != mIcSignalRunnable);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void runOnIcThread(final Handler uiHandler,
|
||||
final GeckoEditableClient client,
|
||||
final Runnable runnable) {
|
||||
final Handler icHandler = client.getInputConnectionHandler();
|
||||
if (icHandler.getLooper() == uiHandler.getLooper()) {
|
||||
// IC thread is UI thread; safe to run directly
|
||||
runnable.run();
|
||||
return;
|
||||
}
|
||||
runOnIcThread(icHandler, runnable);
|
||||
}
|
||||
|
||||
public void sendKeyEventFromUiThread(final Handler uiHandler,
|
||||
final GeckoEditableClient client,
|
||||
final KeyEvent event,
|
||||
final int action,
|
||||
final int metaState) {
|
||||
runOnIcThread(uiHandler, client, new Runnable() {
|
||||
@Override public void run() {
|
||||
client.sendKeyEvent(event, action, metaState);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Editable getEditableForUiThread(final Handler uiHandler,
|
||||
final GeckoEditableClient client) {
|
||||
if (DEBUG) {
|
||||
ThreadUtils.assertOnThread(uiHandler.getLooper().getThread(), AssertBehavior.THROW);
|
||||
}
|
||||
final Handler icHandler = client.getInputConnectionHandler();
|
||||
if (icHandler.getLooper() == uiHandler.getLooper()) {
|
||||
// IC thread is UI thread; safe to use Editable directly
|
||||
return client.getEditable();
|
||||
}
|
||||
// IC thread is not UI thread; we need to return a proxy Editable in order
|
||||
// to safely use the Editable from the UI thread
|
||||
if (mUiEditable != null) {
|
||||
return mUiEditable;
|
||||
}
|
||||
final InvocationHandler invokeEditable = new InvocationHandler() {
|
||||
@Override public Object invoke(final Object proxy,
|
||||
final Method method,
|
||||
final Object[] args) throws Throwable {
|
||||
if (DEBUG) {
|
||||
ThreadUtils.assertOnThread(uiHandler.getLooper().getThread(), AssertBehavior.THROW);
|
||||
Log.d(LOGTAG, "UiEditable." + method.getName() + "() blocking");
|
||||
}
|
||||
synchronized (icHandler) {
|
||||
// Now we are on UI thread
|
||||
mUiEditableReturn = null;
|
||||
mUiEditableException = null;
|
||||
// Post a Runnable that calls the real Editable and saves any
|
||||
// result/exception. Then wait on the Runnable to finish
|
||||
runOnIcThread(icHandler, new Runnable() {
|
||||
@Override public void run() {
|
||||
synchronized (icHandler) {
|
||||
try {
|
||||
mUiEditableReturn = method.invoke(
|
||||
client.getEditable(), args);
|
||||
} catch (Exception e) {
|
||||
mUiEditableException = e;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "UiEditable." + method.getName() +
|
||||
"() returning");
|
||||
}
|
||||
icHandler.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
// let InterruptedException propagate
|
||||
icHandler.wait();
|
||||
if (mUiEditableException != null) {
|
||||
throw mUiEditableException;
|
||||
}
|
||||
return mUiEditableReturn;
|
||||
}
|
||||
}
|
||||
};
|
||||
mUiEditable = (Editable) Proxy.newProxyInstance(Editable.class.getClassLoader(),
|
||||
new Class<?>[] { Editable.class }, invokeEditable);
|
||||
return mUiEditable;
|
||||
}
|
||||
}
|
||||
|
||||
// Managed only by notifyIMEContext; see comments in notifyIMEContext
|
||||
private int mIMEState;
|
||||
private String mIMETypeHint = "";
|
||||
@ -863,70 +702,37 @@ class GeckoInputConnection
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (GamepadUtils.isSonyXperiaGamepadKeyEvent(event)) {
|
||||
return GamepadUtils.translateSonyXperiaGamepadKeys(keyCode, event);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
private boolean processKey(int keyCode, KeyEvent event, boolean down) {
|
||||
if (GamepadUtils.isSonyXperiaGamepadKeyEvent(event)) {
|
||||
event = GamepadUtils.translateSonyXperiaGamepadKeys(keyCode, event);
|
||||
keyCode = event.getKeyCode();
|
||||
}
|
||||
private boolean processKey(final int action, final int keyCode, final KeyEvent event) {
|
||||
|
||||
if (keyCode > KeyEvent.getMaxKeyCode() ||
|
||||
!shouldProcessKey(keyCode, event)) {
|
||||
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.sendKeyEventFromUiThread(
|
||||
ThreadUtils.getUiHandler(), mEditableClient, event, action, /* metaState */ 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.
|
||||
getEditableForUiThread(uiHandler, mEditableClient);
|
||||
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.sendKeyEventFromUiThread(
|
||||
uiHandler, mEditableClient,
|
||||
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);
|
||||
mEditableClient.postToInputConnection(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendKeyEvent(action, event);
|
||||
}
|
||||
}
|
||||
if (down) {
|
||||
mEditableClient.setSuppressKeyUp(false);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return processKey(keyCode, event, true);
|
||||
return processKey(KeyEvent.ACTION_DOWN, keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
return processKey(keyCode, event, false);
|
||||
return processKey(KeyEvent.ACTION_UP, keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -955,8 +761,8 @@ class GeckoInputConnection
|
||||
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)) {
|
||||
if (!processKey(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_UNKNOWN, charEvent) ||
|
||||
!processKey(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_UNKNOWN, charEvent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -964,8 +770,8 @@ class GeckoInputConnection
|
||||
}
|
||||
|
||||
while ((repeatCount--) != 0) {
|
||||
if (!processKey(keyCode, event, /* down */ true) ||
|
||||
!processKey(keyCode, event, /* down */ false)) {
|
||||
if (!processKey(KeyEvent.ACTION_DOWN, keyCode, event) ||
|
||||
!processKey(KeyEvent.ACTION_UP, keyCode, event)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user