am 75a91389: Merge "Implement native key pre-dispatching to IMEs." into gingerbread
Merge commit '75a91389f1938214397aaee262f4136e6bb6b094' into gingerbread-plus-aosp * commit '75a91389f1938214397aaee262f4136e6bb6b094': Implement native key pre-dispatching to IMEs.
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
package android.app;
|
package android.app;
|
||||||
|
|
||||||
|
import com.android.internal.view.IInputMethodCallback;
|
||||||
|
import com.android.internal.view.IInputMethodSession;
|
||||||
|
|
||||||
import dalvik.system.PathClassLoader;
|
import dalvik.system.PathClassLoader;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -25,6 +28,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
|||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience for implementing an activity that will be implemented
|
* Convenience for implementing an activity that will be implemented
|
||||||
@@ -36,6 +40,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
|
|||||||
|
|
||||||
private NativeContentView mNativeContentView;
|
private NativeContentView mNativeContentView;
|
||||||
private InputMethodManager mIMM;
|
private InputMethodManager mIMM;
|
||||||
|
private InputMethodCallback mInputMethodCallback;
|
||||||
|
|
||||||
private int mNativeHandle;
|
private int mNativeHandle;
|
||||||
|
|
||||||
@@ -73,6 +78,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
|
|||||||
private native void onInputChannelDestroyedNative(int handle, InputChannel channel);
|
private native void onInputChannelDestroyedNative(int handle, InputChannel channel);
|
||||||
private native void onContentRectChangedNative(int handle, int x, int y, int w, int h);
|
private native void onContentRectChangedNative(int handle, int x, int y, int w, int h);
|
||||||
private native void dispatchKeyEventNative(int handle, KeyEvent event);
|
private native void dispatchKeyEventNative(int handle, KeyEvent event);
|
||||||
|
private native void finishPreDispatchKeyEventNative(int handle, int seq, boolean handled);
|
||||||
|
|
||||||
static class NativeContentView extends View {
|
static class NativeContentView extends View {
|
||||||
NativeActivity mActivity;
|
NativeActivity mActivity;
|
||||||
@@ -86,12 +92,34 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class InputMethodCallback extends IInputMethodCallback.Stub {
|
||||||
|
WeakReference<NativeActivity> mNa;
|
||||||
|
|
||||||
|
InputMethodCallback(NativeActivity na) {
|
||||||
|
mNa = new WeakReference<NativeActivity>(na);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishedEvent(int seq, boolean handled) {
|
||||||
|
NativeActivity na = mNa.get();
|
||||||
|
if (na != null) {
|
||||||
|
na.finishPreDispatchKeyEventNative(na.mNativeHandle, seq, handled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sessionCreated(IInputMethodSession session) {
|
||||||
|
// Stub -- not for use in the client.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
String libname = "main";
|
String libname = "main";
|
||||||
ActivityInfo ai;
|
ActivityInfo ai;
|
||||||
|
|
||||||
mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
|
mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
mInputMethodCallback = new InputMethodCallback(this);
|
||||||
|
|
||||||
getWindow().takeSurface(this);
|
getWindow().takeSurface(this);
|
||||||
getWindow().takeInputQueue(this);
|
getWindow().takeInputQueue(this);
|
||||||
@@ -292,6 +320,11 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void preDispatchKeyEvent(KeyEvent event, int seq) {
|
||||||
|
mIMM.dispatchKeyEvent(this, seq, event,
|
||||||
|
mInputMethodCallback);
|
||||||
|
}
|
||||||
|
|
||||||
void setWindowFlags(int flags, int mask) {
|
void setWindowFlags(int flags, int mask) {
|
||||||
getWindow().setFlags(flags, mask);
|
getWindow().setFlags(flags, mask);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ static struct {
|
|||||||
jclass clazz;
|
jclass clazz;
|
||||||
|
|
||||||
jmethodID dispatchUnhandledKeyEvent;
|
jmethodID dispatchUnhandledKeyEvent;
|
||||||
|
jmethodID preDispatchKeyEvent;
|
||||||
jmethodID setWindowFlags;
|
jmethodID setWindowFlags;
|
||||||
jmethodID setWindowFormat;
|
jmethodID setWindowFormat;
|
||||||
jmethodID showIme;
|
jmethodID showIme;
|
||||||
@@ -104,7 +105,7 @@ static bool read_work(int fd, ActivityWork* outWork) {
|
|||||||
using namespace android;
|
using namespace android;
|
||||||
|
|
||||||
AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) :
|
AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) :
|
||||||
mWorkWrite(workWrite), mConsumer(channel) {
|
mWorkWrite(workWrite), mConsumer(channel), mSeq(0) {
|
||||||
int msgpipe[2];
|
int msgpipe[2];
|
||||||
if (pipe(msgpipe)) {
|
if (pipe(msgpipe)) {
|
||||||
LOGW("could not create pipe: %s", strerror(errno));
|
LOGW("could not create pipe: %s", strerror(errno));
|
||||||
@@ -157,6 +158,8 @@ int32_t AInputQueue::hasEvents() {
|
|||||||
int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
|
int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
|
||||||
*outEvent = NULL;
|
*outEvent = NULL;
|
||||||
|
|
||||||
|
bool finishNow = false;
|
||||||
|
|
||||||
char byteread;
|
char byteread;
|
||||||
ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
|
ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
|
||||||
if (nRead == 1) {
|
if (nRead == 1) {
|
||||||
@@ -165,10 +168,34 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
|
|||||||
KeyEvent* kevent = mDispatchingKeys[0];
|
KeyEvent* kevent = mDispatchingKeys[0];
|
||||||
*outEvent = kevent;
|
*outEvent = kevent;
|
||||||
mDispatchingKeys.removeAt(0);
|
mDispatchingKeys.removeAt(0);
|
||||||
mDeliveringKeys.add(kevent);
|
in_flight_event inflight;
|
||||||
|
inflight.event = kevent;
|
||||||
|
inflight.seq = -1;
|
||||||
|
inflight.doFinish = false;
|
||||||
|
mInFlightEvents.push(inflight);
|
||||||
|
}
|
||||||
|
if (mFinishPreDispatches.size() > 0) {
|
||||||
|
finish_pre_dispatch finish(mFinishPreDispatches[0]);
|
||||||
|
mFinishPreDispatches.removeAt(0);
|
||||||
|
const size_t N = mInFlightEvents.size();
|
||||||
|
for (size_t i=0; i<N; i++) {
|
||||||
|
const in_flight_event& inflight(mInFlightEvents[i]);
|
||||||
|
if (inflight.seq == finish.seq) {
|
||||||
|
*outEvent = inflight.event;
|
||||||
|
finishNow = finish.handled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*outEvent == NULL) {
|
||||||
|
LOGW("getEvent couldn't find inflight for seq %d", finish.seq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mLock.unlock();
|
mLock.unlock();
|
||||||
if (*outEvent != NULL) {
|
|
||||||
|
if (finishNow) {
|
||||||
|
finishEvent(*outEvent, true);
|
||||||
|
*outEvent = NULL;
|
||||||
|
return -1;
|
||||||
|
} else if (*outEvent != NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,7 +208,7 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
InputEvent* myEvent = NULL;
|
InputEvent* myEvent = NULL;
|
||||||
res = mConsumer.consume(&mInputEventFactory, &myEvent);
|
res = mConsumer.consume(this, &myEvent);
|
||||||
if (res != android::OK) {
|
if (res != android::OK) {
|
||||||
LOGW("channel '%s' ~ Failed to consume input event. status=%d",
|
LOGW("channel '%s' ~ Failed to consume input event. status=%d",
|
||||||
mConsumer.getChannel()->getName().string(), res);
|
mConsumer.getChannel()->getName().string(), res);
|
||||||
@@ -189,39 +216,69 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
in_flight_event inflight;
|
||||||
|
inflight.event = myEvent;
|
||||||
|
inflight.seq = -1;
|
||||||
|
inflight.doFinish = true;
|
||||||
|
mInFlightEvents.push(inflight);
|
||||||
|
|
||||||
*outEvent = myEvent;
|
*outEvent = myEvent;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AInputQueue::preDispatchEvent(AInputEvent* event) {
|
||||||
|
if (((InputEvent*)event)->getType() != AINPUT_EVENT_TYPE_KEY) {
|
||||||
|
// The IME only cares about key events.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now we only send system keys to the IME... this avoids having
|
||||||
|
// critical keys like DPAD go through this path. We really need to have
|
||||||
|
// the IME report which keys it wants.
|
||||||
|
if (!((KeyEvent*)event)->isSystemKey()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return preDispatchKey((KeyEvent*)event);
|
||||||
|
}
|
||||||
|
|
||||||
void AInputQueue::finishEvent(AInputEvent* event, bool handled) {
|
void AInputQueue::finishEvent(AInputEvent* event, bool handled) {
|
||||||
bool needFinished = true;
|
LOG_TRACE("finishEvent: %p handled=%d", event, handled ? 1 : 0);
|
||||||
|
|
||||||
if (!handled && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
|
if (!handled && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
|
||||||
&& ((KeyEvent*)event)->hasDefaultAction()) {
|
&& ((KeyEvent*)event)->hasDefaultAction()) {
|
||||||
// The app didn't handle this, but it may have a default action
|
// The app didn't handle this, but it may have a default action
|
||||||
// associated with it. We need to hand this back to Java to be
|
// associated with it. We need to hand this back to Java to be
|
||||||
// executed.
|
// executed.
|
||||||
doDefaultKey((KeyEvent*)event);
|
doUnhandledKey((KeyEvent*)event);
|
||||||
needFinished = false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t N = mDeliveringKeys.size();
|
mLock.lock();
|
||||||
|
const size_t N = mInFlightEvents.size();
|
||||||
for (size_t i=0; i<N; i++) {
|
for (size_t i=0; i<N; i++) {
|
||||||
if (mDeliveringKeys[i] == event) {
|
const in_flight_event& inflight(mInFlightEvents[i]);
|
||||||
delete event;
|
if (inflight.event == event) {
|
||||||
mDeliveringKeys.removeAt(i);
|
if (inflight.doFinish) {
|
||||||
needFinished = false;
|
int32_t res = mConsumer.sendFinishedSignal();
|
||||||
break;
|
if (res != android::OK) {
|
||||||
|
LOGW("Failed to send finished signal on channel '%s'. status=%d",
|
||||||
|
mConsumer.getChannel()->getName().string(), res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (static_cast<InputEvent*>(event)->getType() == AINPUT_EVENT_TYPE_KEY) {
|
||||||
|
mAvailKeyEvents.push(static_cast<KeyEvent*>(event));
|
||||||
|
} else {
|
||||||
|
mAvailMotionEvents.push(static_cast<MotionEvent*>(event));
|
||||||
|
}
|
||||||
|
mInFlightEvents.removeAt(i);
|
||||||
|
mLock.unlock();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mLock.unlock();
|
||||||
|
|
||||||
if (needFinished) {
|
LOGW("finishEvent called for unknown event: %p", event);
|
||||||
int32_t res = mConsumer.sendFinishedSignal();
|
|
||||||
if (res != android::OK) {
|
|
||||||
LOGW("Failed to send finished signal on channel '%s'. status=%d",
|
|
||||||
mConsumer.getChannel()->getName().string(), res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AInputQueue::dispatchEvent(android::KeyEvent* event) {
|
void AInputQueue::dispatchEvent(android::KeyEvent* event) {
|
||||||
@@ -229,8 +286,120 @@ void AInputQueue::dispatchEvent(android::KeyEvent* event) {
|
|||||||
LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(),
|
LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(),
|
||||||
mDispatchKeyWrite);
|
mDispatchKeyWrite);
|
||||||
mDispatchingKeys.add(event);
|
mDispatchingKeys.add(event);
|
||||||
|
wakeupDispatch();
|
||||||
mLock.unlock();
|
mLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AInputQueue::finishPreDispatch(int seq, bool handled) {
|
||||||
|
mLock.lock();
|
||||||
|
LOG_TRACE("finishPreDispatch: seq=%d handled=%d\n", seq, handled ? 1 : 0);
|
||||||
|
finish_pre_dispatch finish;
|
||||||
|
finish.seq = seq;
|
||||||
|
finish.handled = handled;
|
||||||
|
mFinishPreDispatches.add(finish);
|
||||||
|
wakeupDispatch();
|
||||||
|
mLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEvent* AInputQueue::consumeUnhandledEvent() {
|
||||||
|
KeyEvent* event = NULL;
|
||||||
|
|
||||||
|
mLock.lock();
|
||||||
|
if (mUnhandledKeys.size() > 0) {
|
||||||
|
event = mUnhandledKeys[0];
|
||||||
|
mUnhandledKeys.removeAt(0);
|
||||||
|
}
|
||||||
|
mLock.unlock();
|
||||||
|
|
||||||
|
LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event);
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) {
|
||||||
|
KeyEvent* event = NULL;
|
||||||
|
|
||||||
|
mLock.lock();
|
||||||
|
if (mPreDispatchingKeys.size() > 0) {
|
||||||
|
const in_flight_event& inflight(mPreDispatchingKeys[0]);
|
||||||
|
event = static_cast<KeyEvent*>(inflight.event);
|
||||||
|
*outSeq = inflight.seq;
|
||||||
|
mPreDispatchingKeys.removeAt(0);
|
||||||
|
}
|
||||||
|
mLock.unlock();
|
||||||
|
|
||||||
|
LOG_TRACE("consumePreDispatchingEvent: KeyEvent=%p", event);
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEvent* AInputQueue::createKeyEvent() {
|
||||||
|
mLock.lock();
|
||||||
|
KeyEvent* event;
|
||||||
|
if (mAvailKeyEvents.size() <= 0) {
|
||||||
|
event = new KeyEvent();
|
||||||
|
} else {
|
||||||
|
event = mAvailKeyEvents.top();
|
||||||
|
mAvailKeyEvents.pop();
|
||||||
|
}
|
||||||
|
mLock.unlock();
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent* AInputQueue::createMotionEvent() {
|
||||||
|
mLock.lock();
|
||||||
|
MotionEvent* event;
|
||||||
|
if (mAvailMotionEvents.size() <= 0) {
|
||||||
|
event = new MotionEvent();
|
||||||
|
} else {
|
||||||
|
event = mAvailMotionEvents.top();
|
||||||
|
mAvailMotionEvents.pop();
|
||||||
|
}
|
||||||
|
mLock.unlock();
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AInputQueue::doUnhandledKey(KeyEvent* keyEvent) {
|
||||||
|
mLock.lock();
|
||||||
|
LOG_TRACE("Unhandled key: pending=%d write=%d\n", mUnhandledKeys.size(), mWorkWrite);
|
||||||
|
if (mUnhandledKeys.size() <= 0 && mWorkWrite >= 0) {
|
||||||
|
write_work(mWorkWrite, CMD_DEF_KEY);
|
||||||
|
}
|
||||||
|
mUnhandledKeys.add(keyEvent);
|
||||||
|
mLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
|
||||||
|
mLock.lock();
|
||||||
|
LOG_TRACE("preDispatch key: pending=%d write=%d\n", mPreDispatchingKeys.size(), mWorkWrite);
|
||||||
|
const size_t N = mInFlightEvents.size();
|
||||||
|
for (size_t i=0; i<N; i++) {
|
||||||
|
in_flight_event& inflight(mInFlightEvents.editItemAt(i));
|
||||||
|
if (inflight.event == keyEvent) {
|
||||||
|
if (inflight.seq >= 0) {
|
||||||
|
// This event has already been pre-dispatched!
|
||||||
|
LOG_TRACE("Event already pre-dispatched!");
|
||||||
|
mLock.unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mSeq++;
|
||||||
|
if (mSeq < 0) mSeq = 1;
|
||||||
|
inflight.seq = mSeq;
|
||||||
|
|
||||||
|
if (mPreDispatchingKeys.size() <= 0 && mWorkWrite >= 0) {
|
||||||
|
write_work(mWorkWrite, CMD_DEF_KEY);
|
||||||
|
}
|
||||||
|
mPreDispatchingKeys.add(inflight);
|
||||||
|
mLock.unlock();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGW("preDispatchKey called for unknown event: %p", keyEvent);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AInputQueue::wakeupDispatch() {
|
||||||
restart:
|
restart:
|
||||||
char dummy = 0;
|
char dummy = 0;
|
||||||
int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
|
int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
|
||||||
@@ -244,31 +413,6 @@ restart:
|
|||||||
else LOGW("Truncated writing to dispatch fd: %d", res);
|
else LOGW("Truncated writing to dispatch fd: %d", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent* AInputQueue::consumeUnhandledEvent() {
|
|
||||||
KeyEvent* event = NULL;
|
|
||||||
|
|
||||||
mLock.lock();
|
|
||||||
if (mPendingKeys.size() > 0) {
|
|
||||||
event = mPendingKeys[0];
|
|
||||||
mPendingKeys.removeAt(0);
|
|
||||||
}
|
|
||||||
mLock.unlock();
|
|
||||||
|
|
||||||
LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event);
|
|
||||||
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AInputQueue::doDefaultKey(KeyEvent* keyEvent) {
|
|
||||||
mLock.lock();
|
|
||||||
LOG_TRACE("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite);
|
|
||||||
if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) {
|
|
||||||
write_work(mWorkWrite, CMD_DEF_KEY);
|
|
||||||
}
|
|
||||||
mPendingKeys.add(keyEvent);
|
|
||||||
mLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@@ -417,11 +561,14 @@ static bool mainWorkCallback(int fd, int events, void* data) {
|
|||||||
code->env, keyEvent);
|
code->env, keyEvent);
|
||||||
code->env->CallVoidMethod(code->clazz,
|
code->env->CallVoidMethod(code->clazz,
|
||||||
gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
|
gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
|
||||||
int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
|
code->nativeInputQueue->finishEvent(keyEvent, true);
|
||||||
if (res != OK) {
|
}
|
||||||
LOGW("Failed to send finished signal on channel '%s'. status=%d",
|
int seq;
|
||||||
code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
|
while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
|
||||||
}
|
jobject inputEventObj = android_view_KeyEvent_fromNative(
|
||||||
|
code->env, keyEvent);
|
||||||
|
code->env->CallVoidMethod(code->clazz,
|
||||||
|
gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case CMD_SET_WINDOW_FORMAT: {
|
case CMD_SET_WINDOW_FORMAT: {
|
||||||
@@ -766,13 +913,26 @@ dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventOb
|
|||||||
if (handle != 0) {
|
if (handle != 0) {
|
||||||
NativeCode* code = (NativeCode*)handle;
|
NativeCode* code = (NativeCode*)handle;
|
||||||
if (code->nativeInputQueue != NULL) {
|
if (code->nativeInputQueue != NULL) {
|
||||||
KeyEvent* event = new KeyEvent();
|
KeyEvent* event = code->nativeInputQueue->createKeyEvent();
|
||||||
android_view_KeyEvent_toNative(env, eventObj, event);
|
android_view_KeyEvent_toNative(env, eventObj, event);
|
||||||
code->nativeInputQueue->dispatchEvent(event);
|
code->nativeInputQueue->dispatchEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle,
|
||||||
|
jint seq, jboolean handled)
|
||||||
|
{
|
||||||
|
LOG_TRACE("finishPreDispatchKeyEvent_native");
|
||||||
|
if (handle != 0) {
|
||||||
|
NativeCode* code = (NativeCode*)handle;
|
||||||
|
if (code->nativeInputQueue != NULL) {
|
||||||
|
code->nativeInputQueue->finishPreDispatch(seq, handled ? true : false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const JNINativeMethod g_methods[] = {
|
static const JNINativeMethod g_methods[] = {
|
||||||
{ "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I",
|
{ "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I",
|
||||||
(void*)loadNativeCode_native },
|
(void*)loadNativeCode_native },
|
||||||
@@ -792,6 +952,7 @@ static const JNINativeMethod g_methods[] = {
|
|||||||
{ "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
|
{ "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
|
||||||
{ "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
|
{ "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
|
||||||
{ "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
|
{ "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
|
||||||
|
{ "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* const kNativeActivityPathName = "android/app/NativeActivity";
|
static const char* const kNativeActivityPathName = "android/app/NativeActivity";
|
||||||
@@ -814,6 +975,9 @@ int register_android_app_NativeActivity(JNIEnv* env)
|
|||||||
GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
|
GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
|
||||||
gNativeActivityClassInfo.clazz,
|
gNativeActivityClassInfo.clazz,
|
||||||
"dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
|
"dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
|
||||||
|
GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
|
||||||
|
gNativeActivityClassInfo.clazz,
|
||||||
|
"preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
|
||||||
|
|
||||||
GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
|
GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
|
||||||
gNativeActivityClassInfo.clazz,
|
gNativeActivityClassInfo.clazz,
|
||||||
|
|||||||
@@ -42,8 +42,26 @@ extern void android_NativeActivity_hideSoftInput(
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* NDK input queue API.
|
* NDK input queue API.
|
||||||
|
*
|
||||||
|
* Here is the event flow:
|
||||||
|
* 1. Event arrives in input consumer, and is returned by getEvent().
|
||||||
|
* 2. Application calls preDispatchEvent():
|
||||||
|
* a. Event is assigned a sequence ID and enqueued in mPreDispatchingKeys.
|
||||||
|
* b. Main thread picks up event, hands to input method.
|
||||||
|
* c. Input method eventually returns sequence # and whether it was handled.
|
||||||
|
* d. finishPreDispatch() is called to enqueue the information.
|
||||||
|
* e. next getEvent() call will:
|
||||||
|
* - finish any pre-dispatch events that the input method handled
|
||||||
|
* - return the next pre-dispatched event that the input method didn't handle.
|
||||||
|
* f. (A preDispatchEvent() call on this event will now return false).
|
||||||
|
* 3. Application calls finishEvent() with whether it was handled.
|
||||||
|
* - If handled is true, the event is finished.
|
||||||
|
* - If handled is false, the event is put on mUnhandledKeys, and:
|
||||||
|
* a. Main thread receives event from consumeUnhandledEvent().
|
||||||
|
* b. Java sends event through default key handler.
|
||||||
|
* c. event is finished.
|
||||||
*/
|
*/
|
||||||
struct AInputQueue {
|
struct AInputQueue : public android::InputEventFactoryInterface {
|
||||||
public:
|
public:
|
||||||
/* Creates a consumer associated with an input channel. */
|
/* Creates a consumer associated with an input channel. */
|
||||||
explicit AInputQueue(const android::sp<android::InputChannel>& channel, int workWrite);
|
explicit AInputQueue(const android::sp<android::InputChannel>& channel, int workWrite);
|
||||||
@@ -59,8 +77,9 @@ public:
|
|||||||
|
|
||||||
int32_t getEvent(AInputEvent** outEvent);
|
int32_t getEvent(AInputEvent** outEvent);
|
||||||
|
|
||||||
void finishEvent(AInputEvent* event, bool handled);
|
bool preDispatchEvent(AInputEvent* event);
|
||||||
|
|
||||||
|
void finishEvent(AInputEvent* event, bool handled);
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
@@ -68,28 +87,63 @@ public:
|
|||||||
|
|
||||||
void dispatchEvent(android::KeyEvent* event);
|
void dispatchEvent(android::KeyEvent* event);
|
||||||
|
|
||||||
|
void finishPreDispatch(int seq, bool handled);
|
||||||
|
|
||||||
android::KeyEvent* consumeUnhandledEvent();
|
android::KeyEvent* consumeUnhandledEvent();
|
||||||
|
android::KeyEvent* consumePreDispatchingEvent(int* outSeq);
|
||||||
|
|
||||||
|
virtual android::KeyEvent* createKeyEvent();
|
||||||
|
virtual android::MotionEvent* createMotionEvent();
|
||||||
|
|
||||||
int mWorkWrite;
|
int mWorkWrite;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doDefaultKey(android::KeyEvent* keyEvent);
|
void doUnhandledKey(android::KeyEvent* keyEvent);
|
||||||
|
bool preDispatchKey(android::KeyEvent* keyEvent);
|
||||||
|
void wakeupDispatch();
|
||||||
|
|
||||||
android::InputConsumer mConsumer;
|
android::InputConsumer mConsumer;
|
||||||
android::PreallocatedInputEventFactory mInputEventFactory;
|
|
||||||
android::sp<android::PollLoop> mPollLoop;
|
android::sp<android::PollLoop> mPollLoop;
|
||||||
|
|
||||||
int mDispatchKeyRead;
|
int mDispatchKeyRead;
|
||||||
int mDispatchKeyWrite;
|
int mDispatchKeyWrite;
|
||||||
|
|
||||||
// This is only touched by the event reader thread. It is the current
|
struct in_flight_event {
|
||||||
// key events that came out of the mDispatchingKeys list and are now
|
android::InputEvent* event;
|
||||||
//<2F>delivered to the app.
|
int seq;
|
||||||
android::Vector<android::KeyEvent*> mDeliveringKeys;
|
bool doFinish;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct finish_pre_dispatch {
|
||||||
|
int seq;
|
||||||
|
bool handled;
|
||||||
|
};
|
||||||
|
|
||||||
android::Mutex mLock;
|
android::Mutex mLock;
|
||||||
android::Vector<android::KeyEvent*> mPendingKeys;
|
|
||||||
|
int mSeq;
|
||||||
|
|
||||||
|
// Cache of previously allocated key events.
|
||||||
|
android::Vector<android::KeyEvent*> mAvailKeyEvents;
|
||||||
|
// Cache of previously allocated motion events.
|
||||||
|
android::Vector<android::MotionEvent*> mAvailMotionEvents;
|
||||||
|
|
||||||
|
// All input events that are actively being processed.
|
||||||
|
android::Vector<in_flight_event> mInFlightEvents;
|
||||||
|
|
||||||
|
// Key events that the app didn't handle, and are pending for
|
||||||
|
// delivery to the activity's default key handling.
|
||||||
|
android::Vector<android::KeyEvent*> mUnhandledKeys;
|
||||||
|
|
||||||
|
// Keys that arrived in the Java framework and need to be
|
||||||
|
// dispatched to the app.
|
||||||
android::Vector<android::KeyEvent*> mDispatchingKeys;
|
android::Vector<android::KeyEvent*> mDispatchingKeys;
|
||||||
|
|
||||||
|
// Key events that are pending to be pre-dispatched to the IME.
|
||||||
|
android::Vector<in_flight_event> mPreDispatchingKeys;
|
||||||
|
|
||||||
|
// Event sequence numbers that we have finished pre-dispatching.
|
||||||
|
android::Vector<finish_pre_dispatch> mFinishPreDispatches;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _ANDROID_APP_NATIVEACTIVITY_H
|
#endif // _ANDROID_APP_NATIVEACTIVITY_H
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initialize(int32_t deviceId, int32_t source);
|
void initialize(int32_t deviceId, int32_t source);
|
||||||
|
void initialize(const InputEvent& from);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int32_t mDeviceId;
|
int32_t mDeviceId;
|
||||||
@@ -202,6 +203,7 @@ public:
|
|||||||
int32_t repeatCount,
|
int32_t repeatCount,
|
||||||
nsecs_t downTime,
|
nsecs_t downTime,
|
||||||
nsecs_t eventTime);
|
nsecs_t eventTime);
|
||||||
|
void initialize(const KeyEvent& from);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int32_t mAction;
|
int32_t mAction;
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ void InputEvent::initialize(int32_t deviceId, int32_t source) {
|
|||||||
mSource = source;
|
mSource = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputEvent::initialize(const InputEvent& from) {
|
||||||
|
mDeviceId = from.mDeviceId;
|
||||||
|
mSource = from.mSource;
|
||||||
|
}
|
||||||
|
|
||||||
// class KeyEvent
|
// class KeyEvent
|
||||||
|
|
||||||
bool KeyEvent::hasDefaultAction(int32_t keyCode) {
|
bool KeyEvent::hasDefaultAction(int32_t keyCode) {
|
||||||
@@ -106,6 +111,18 @@ void KeyEvent::initialize(
|
|||||||
mEventTime = eventTime;
|
mEventTime = eventTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KeyEvent::initialize(const KeyEvent& from) {
|
||||||
|
InputEvent::initialize(from);
|
||||||
|
mAction = from.mAction;
|
||||||
|
mFlags = from.mFlags;
|
||||||
|
mKeyCode = from.mKeyCode;
|
||||||
|
mScanCode = from.mScanCode;
|
||||||
|
mMetaState = from.mMetaState;
|
||||||
|
mRepeatCount = from.mRepeatCount;
|
||||||
|
mDownTime = from.mDownTime;
|
||||||
|
mEventTime = from.mEventTime;
|
||||||
|
}
|
||||||
|
|
||||||
// class MotionEvent
|
// class MotionEvent
|
||||||
|
|
||||||
void MotionEvent::initialize(
|
void MotionEvent::initialize(
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ void AInputQueue_detachLooper(AInputQueue* queue) {
|
|||||||
queue->detachLooper();
|
queue->detachLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AInputQueue_hasEvents(AInputQueue* queue) {
|
int32_t AInputQueue_hasEvents(AInputQueue* queue) {
|
||||||
return queue->hasEvents();
|
return queue->hasEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +256,10 @@ int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent) {
|
|||||||
return queue->getEvent(outEvent);
|
return queue->getEvent(outEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event) {
|
||||||
|
return queue->preDispatchEvent(event) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) {
|
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) {
|
||||||
queue->finishEvent(event, handled != 0);
|
queue->finishEvent(event, handled != 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -607,7 +607,7 @@ void AInputQueue_detachLooper(AInputQueue* queue);
|
|||||||
* input queue. Returns 1 if the queue has events; 0 if
|
* input queue. Returns 1 if the queue has events; 0 if
|
||||||
* it does not have events; and a negative value if there is an error.
|
* it does not have events; and a negative value if there is an error.
|
||||||
*/
|
*/
|
||||||
int AInputQueue_hasEvents(AInputQueue* queue);
|
int32_t AInputQueue_hasEvents(AInputQueue* queue);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the next available event from the queue. Returns a negative
|
* Returns the next available event from the queue. Returns a negative
|
||||||
@@ -615,6 +615,16 @@ int AInputQueue_hasEvents(AInputQueue* queue);
|
|||||||
*/
|
*/
|
||||||
int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent);
|
int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends the key for standard pre-dispatching -- that is, possibly deliver
|
||||||
|
* it to the current IME to be consumed before the app. Returns 0 if it
|
||||||
|
* was not pre-dispatched, meaning you can process it right now. If non-zero
|
||||||
|
* is returned, you must abandon the current event processing and allow the
|
||||||
|
* event to appear again in the event queue (if it does not get consumed during
|
||||||
|
* pre-dispatching).
|
||||||
|
*/
|
||||||
|
int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Report that dispatching has finished with the given event.
|
* Report that dispatching has finished with the given event.
|
||||||
* This must be called after receiving an event with AInputQueue_get_event().
|
* This must be called after receiving an event with AInputQueue_get_event().
|
||||||
|
|||||||
Reference in New Issue
Block a user