Merge "Implement native key pre-dispatching to IMEs." into gingerbread

This commit is contained in:
Dianne Hackborn
2010-07-15 22:22:42 -07:00
committed by Android (Google) Code Review
7 changed files with 347 additions and 63 deletions

View File

@@ -1,5 +1,8 @@
package android.app;
import com.android.internal.view.IInputMethodCallback;
import com.android.internal.view.IInputMethodSession;
import dalvik.system.PathClassLoader;
import android.content.Context;
@@ -25,6 +28,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.inputmethod.InputMethodManager;
import java.io.File;
import java.lang.ref.WeakReference;
/**
* 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 InputMethodManager mIMM;
private InputMethodCallback mInputMethodCallback;
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 onContentRectChangedNative(int handle, int x, int y, int w, int h);
private native void dispatchKeyEventNative(int handle, KeyEvent event);
private native void finishPreDispatchKeyEventNative(int handle, int seq, boolean handled);
static class NativeContentView extends View {
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
protected void onCreate(Bundle savedInstanceState) {
String libname = "main";
ActivityInfo ai;
mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
mInputMethodCallback = new InputMethodCallback(this);
getWindow().takeSurface(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) {
getWindow().setFlags(flags, mask);
}

View File

@@ -45,6 +45,7 @@ static struct {
jclass clazz;
jmethodID dispatchUnhandledKeyEvent;
jmethodID preDispatchKeyEvent;
jmethodID setWindowFlags;
jmethodID setWindowFormat;
jmethodID showIme;
@@ -104,7 +105,7 @@ static bool read_work(int fd, ActivityWork* outWork) {
using namespace android;
AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) :
mWorkWrite(workWrite), mConsumer(channel) {
mWorkWrite(workWrite), mConsumer(channel), mSeq(0) {
int msgpipe[2];
if (pipe(msgpipe)) {
LOGW("could not create pipe: %s", strerror(errno));
@@ -157,6 +158,8 @@ int32_t AInputQueue::hasEvents() {
int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
*outEvent = NULL;
bool finishNow = false;
char byteread;
ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
if (nRead == 1) {
@@ -165,10 +168,34 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
KeyEvent* kevent = mDispatchingKeys[0];
*outEvent = kevent;
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();
if (*outEvent != NULL) {
if (finishNow) {
finishEvent(*outEvent, true);
*outEvent = NULL;
return -1;
} else if (*outEvent != NULL) {
return 0;
}
}
@@ -181,7 +208,7 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
}
InputEvent* myEvent = NULL;
res = mConsumer.consume(&mInputEventFactory, &myEvent);
res = mConsumer.consume(this, &myEvent);
if (res != android::OK) {
LOGW("channel '%s' ~ Failed to consume input event. status=%d",
mConsumer.getChannel()->getName().string(), res);
@@ -189,39 +216,69 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
return -1;
}
in_flight_event inflight;
inflight.event = myEvent;
inflight.seq = -1;
inflight.doFinish = true;
mInFlightEvents.push(inflight);
*outEvent = myEvent;
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) {
bool needFinished = true;
LOG_TRACE("finishEvent: %p handled=%d", event, handled ? 1 : 0);
if (!handled && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
&& ((KeyEvent*)event)->hasDefaultAction()) {
// 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
// executed.
doDefaultKey((KeyEvent*)event);
needFinished = false;
doUnhandledKey((KeyEvent*)event);
return;
}
const size_t N = mDeliveringKeys.size();
mLock.lock();
const size_t N = mInFlightEvents.size();
for (size_t i=0; i<N; i++) {
if (mDeliveringKeys[i] == event) {
delete event;
mDeliveringKeys.removeAt(i);
needFinished = false;
break;
const in_flight_event& inflight(mInFlightEvents[i]);
if (inflight.event == event) {
if (inflight.doFinish) {
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);
}
}
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) {
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);
}
}
LOGW("finishEvent called for unknown event: %p", 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(),
mDispatchKeyWrite);
mDispatchingKeys.add(event);
wakeupDispatch();
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:
char dummy = 0;
int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
@@ -244,31 +413,6 @@ restart:
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 {
// ------------------------------------------------------------------------
@@ -417,11 +561,14 @@ static bool mainWorkCallback(int fd, int events, void* data) {
code->env, keyEvent);
code->env->CallVoidMethod(code->clazz,
gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
if (res != OK) {
LOGW("Failed to send finished signal on channel '%s'. status=%d",
code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
}
code->nativeInputQueue->finishEvent(keyEvent, true);
}
int seq;
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;
case CMD_SET_WINDOW_FORMAT: {
@@ -766,13 +913,26 @@ dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventOb
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->nativeInputQueue != NULL) {
KeyEvent* event = new KeyEvent();
KeyEvent* event = code->nativeInputQueue->createKeyEvent();
android_view_KeyEvent_toNative(env, eventObj, 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[] = {
{ "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I",
(void*)loadNativeCode_native },
@@ -792,6 +952,7 @@ static const JNINativeMethod g_methods[] = {
{ "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
{ "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
{ "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
{ "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native },
};
static const char* const kNativeActivityPathName = "android/app/NativeActivity";
@@ -814,6 +975,9 @@ int register_android_app_NativeActivity(JNIEnv* env)
GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
gNativeActivityClassInfo.clazz,
"dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
gNativeActivityClassInfo.clazz,
"preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
gNativeActivityClassInfo.clazz,

View File

@@ -42,8 +42,26 @@ extern void android_NativeActivity_hideSoftInput(
/*
* 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:
/* Creates a consumer associated with an input channel. */
explicit AInputQueue(const android::sp<android::InputChannel>& channel, int workWrite);
@@ -59,8 +77,9 @@ public:
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 finishPreDispatch(int seq, bool handled);
android::KeyEvent* consumeUnhandledEvent();
android::KeyEvent* consumePreDispatchingEvent(int* outSeq);
virtual android::KeyEvent* createKeyEvent();
virtual android::MotionEvent* createMotionEvent();
int mWorkWrite;
private:
void doDefaultKey(android::KeyEvent* keyEvent);
void doUnhandledKey(android::KeyEvent* keyEvent);
bool preDispatchKey(android::KeyEvent* keyEvent);
void wakeupDispatch();
android::InputConsumer mConsumer;
android::PreallocatedInputEventFactory mInputEventFactory;
android::sp<android::PollLoop> mPollLoop;
int mDispatchKeyRead;
int mDispatchKeyWrite;
// This is only touched by the event reader thread. It is the current
// key events that came out of the mDispatchingKeys list and are now
//<2F>delivered to the app.
android::Vector<android::KeyEvent*> mDeliveringKeys;
struct in_flight_event {
android::InputEvent* event;
int seq;
bool doFinish;
};
struct finish_pre_dispatch {
int seq;
bool handled;
};
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;
// 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

View File

@@ -152,6 +152,7 @@ public:
protected:
void initialize(int32_t deviceId, int32_t source);
void initialize(const InputEvent& from);
private:
int32_t mDeviceId;
@@ -202,6 +203,7 @@ public:
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime);
void initialize(const KeyEvent& from);
private:
int32_t mAction;

View File

@@ -18,6 +18,11 @@ void InputEvent::initialize(int32_t deviceId, int32_t source) {
mSource = source;
}
void InputEvent::initialize(const InputEvent& from) {
mDeviceId = from.mDeviceId;
mSource = from.mSource;
}
// class KeyEvent
bool KeyEvent::hasDefaultAction(int32_t keyCode) {
@@ -106,6 +111,18 @@ void KeyEvent::initialize(
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
void MotionEvent::initialize(

View File

@@ -248,7 +248,7 @@ void AInputQueue_detachLooper(AInputQueue* queue) {
queue->detachLooper();
}
int AInputQueue_hasEvents(AInputQueue* queue) {
int32_t AInputQueue_hasEvents(AInputQueue* queue) {
return queue->hasEvents();
}
@@ -256,6 +256,10 @@ int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** 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) {
queue->finishEvent(event, handled != 0);
}

View File

@@ -607,7 +607,7 @@ void AInputQueue_detachLooper(AInputQueue* queue);
* 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.
*/
int AInputQueue_hasEvents(AInputQueue* queue);
int32_t AInputQueue_hasEvents(AInputQueue* queue);
/*
* 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);
/*
* 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.
* This must be called after receiving an event with AInputQueue_get_event().