Rewrite input handling for native applications

Bug: 8473020
Change-Id: Ic4353d8924ab877bec21aff8c2dba9fe725bf906
This commit is contained in:
Michael Wright
2013-04-10 21:12:00 -07:00
parent c3d0a81a4a
commit a44dd26a75
12 changed files with 585 additions and 592 deletions

View File

@@ -27,7 +27,6 @@ import android.os.Environment;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.AttributeSet;
import android.view.InputChannel;
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.Surface;
@@ -111,11 +110,9 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
int format, int width, int height);
private native void onSurfaceRedrawNeededNative(int handle, Surface surface);
private native void onSurfaceDestroyedNative(int handle);
private native void onInputChannelCreatedNative(int handle, InputChannel channel);
private native void onInputChannelDestroyedNative(int handle, InputChannel channel);
private native void onInputQueueCreatedNative(int handle, int queuePtr);
private native void onInputQueueDestroyedNative(int handle, int queuePtr);
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;
@@ -197,7 +194,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
mCurSurfaceHolder = null;
}
if (mCurInputQueue != null) {
onInputChannelDestroyedNative(mNativeHandle, mCurInputQueue.getInputChannel());
onInputQueueDestroyedNative(mNativeHandle, mCurInputQueue.getNativePtr());
mCurInputQueue = null;
}
unloadNativeCode(mNativeHandle);
@@ -261,18 +258,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mDispatchingUnhandledKey) {
return super.dispatchKeyEvent(event);
} else {
// Key events from the IME do not go through the input channel;
// we need to intercept them here to hand to the application.
dispatchKeyEventNative(mNativeHandle, event);
return true;
}
}
public void surfaceCreated(SurfaceHolder holder) {
if (!mDestroyed) {
mCurSurfaceHolder = holder;
@@ -304,14 +289,14 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
public void onInputQueueCreated(InputQueue queue) {
if (!mDestroyed) {
mCurInputQueue = queue;
onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel());
onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr());
}
}
public void onInputQueueDestroyed(InputQueue queue) {
mCurInputQueue = null;
if (!mDestroyed) {
onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel());
onInputQueueDestroyedNative(mNativeHandle, queue.getNativePtr());
mCurInputQueue = null;
}
}
@@ -332,25 +317,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
}
}
boolean dispatchUnhandledKeyEvent(KeyEvent event) {
try {
mDispatchingUnhandledKey = true;
View decor = getWindow().getDecorView();
if (decor != null) {
return decor.dispatchKeyEvent(event);
} else {
return false;
}
} finally {
mDispatchingUnhandledKey = false;
}
}
void preDispatchKeyEvent(KeyEvent event, int seq) {
// FIXME: Input dispatch should be redirected back through ViewRootImpl again.
finishPreDispatchKeyEventNative(mNativeHandle, seq, false);
}
void setWindowFlags(int flags, int mask) {
getWindow().setFlags(flags, mask);
}

View File

@@ -16,11 +16,127 @@
package android.view;
import dalvik.system.CloseGuard;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Pools.Pool;
import android.util.Pools.SimplePool;
import android.util.SparseArray;
import java.lang.ref.WeakReference;
/**
* An input queue provides a mechanism for an application to receive incoming
* input events. Currently only usable from native code.
*/
public final class InputQueue {
private final SparseArray<ActiveInputEvent> mActiveEventArray =
new SparseArray<ActiveInputEvent>(20);
private final Pool<ActiveInputEvent> mActiveInputEventPool =
new SimplePool<ActiveInputEvent>(20);
private final CloseGuard mCloseGuard = CloseGuard.get();
private int mPtr;
private static native int nativeInit(WeakReference<InputQueue> weakQueue,
MessageQueue messageQueue);
private static native int nativeSendKeyEvent(int ptr, KeyEvent e, boolean preDispatch);
private static native int nativeSendMotionEvent(int ptr, MotionEvent e);
private static native void nativeDispose(int ptr);
/** @hide */
public InputQueue() {
mPtr = nativeInit(new WeakReference<InputQueue>(this), Looper.myQueue());
mCloseGuard.open("dispose");
}
@Override
protected void finalize() throws Throwable {
try {
dispose(true);
} finally {
super.finalize();
}
}
/** @hide */
public void dispose() {
dispose(false);
}
/** @hide */
public void dispose(boolean finalized) {
if (mCloseGuard != null) {
if (finalized) {
mCloseGuard.warnIfOpen();
}
mCloseGuard.close();
}
if (mPtr != 0) {
nativeDispose(mPtr);
mPtr = 0;
}
}
/** @hide */
public int getNativePtr() {
return mPtr;
}
/** @hide */
public void sendInputEvent(InputEvent e, Object token, boolean predispatch,
FinishedInputEventCallback callback) {
ActiveInputEvent event = obtainActiveInputEvent(token, callback);
int id;
if (e instanceof KeyEvent) {
id = nativeSendKeyEvent(mPtr, (KeyEvent) e, predispatch);
} else {
id = nativeSendMotionEvent(mPtr, (MotionEvent) e);
}
mActiveEventArray.put(id, event);
}
private void finishInputEvent(int id, boolean handled) {
int index = mActiveEventArray.indexOfKey(id);
if (index >= 0) {
ActiveInputEvent e = mActiveEventArray.valueAt(index);
mActiveEventArray.removeAt(index);
e.mCallback.onFinishedInputEvent(e.mToken, handled);
recycleActiveInputEvent(e);
}
}
private ActiveInputEvent obtainActiveInputEvent(Object token,
FinishedInputEventCallback callback) {
ActiveInputEvent e = mActiveInputEventPool.acquire();
if (e == null) {
e = new ActiveInputEvent();
}
e.mToken = token;
e.mCallback = callback;
return e;
}
private void recycleActiveInputEvent(ActiveInputEvent e) {
e.recycle();
mActiveInputEventPool.release(e);
}
private final class ActiveInputEvent {
public Object mToken;
public FinishedInputEventCallback mCallback;
public void recycle() {
mToken = null;
mCallback = null;
}
}
/**
* Interface to receive notification of when an InputQueue is associated
* and dissociated with a thread.
@@ -31,7 +147,7 @@ public final class InputQueue {
* thread making this call, so it can start receiving events from it.
*/
void onInputQueueCreated(InputQueue queue);
/**
* Called when the given InputQueue is no longer associated with
* the thread and thus not dispatching events.
@@ -39,15 +155,9 @@ public final class InputQueue {
void onInputQueueDestroyed(InputQueue queue);
}
final InputChannel mChannel;
/** @hide */
public InputQueue(InputChannel channel) {
mChannel = channel;
}
/** @hide */
public InputChannel getInputChannel() {
return mChannel;
public static interface FinishedInputEventCallback {
void onFinishedInputEvent(Object token, boolean handled);
}
}

View File

@@ -1232,6 +1232,12 @@ public class KeyEvent extends InputEvent implements Parcelable {
*/
public static final int FLAG_FALLBACK = 0x400;
/**
* Signifies that the key is being predispatched.
* @hide
*/
public static final int FLAG_PREDISPATCH = 0x20000000;
/**
* Private control to determine when an app is tracking a key sequence.
* @hide

View File

@@ -597,12 +597,11 @@ public final class ViewRootImpl implements ViewParent,
}
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue(mInputChannel);
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
} else {
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
view.assignParent(this);
@@ -2822,9 +2821,11 @@ public final class ViewRootImpl implements ViewParent,
if (mInputQueueCallback != null && mInputQueue != null) {
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
mInputQueue.dispose();
mInputQueueCallback = null;
mInputQueue = null;
} else if (mInputEventReceiver != null) {
}
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
mInputEventReceiver = null;
}
@@ -3347,6 +3348,15 @@ public final class ViewRootImpl implements ViewParent,
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (mView == null || !mAdded) {
Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent);
finish(q, false);
} else if (!mAttachInfo.mHasWindowFocus &&
!q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) &&
!isTerminalInputEvent(q.mEvent)) {
// If this is a focused event and the window doesn't currently have input focus,
// then drop this event. This could be an event that came back from the previous
// stage but the window has lost focus in the meantime.
Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
finish(q, false);
} else {
apply(q, onProcess(q));
@@ -3547,15 +3557,30 @@ public final class ViewRootImpl implements ViewParent,
* Delivers pre-ime input events to a native activity.
* Does not support pointer events.
*/
final class NativePreImeInputStage extends AsyncInputStage {
final class NativePreImeInputStage extends AsyncInputStage
implements InputQueue.FinishedInputEventCallback {
public NativePreImeInputStage(InputStage next, String traceCounter) {
super(next, traceCounter);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
mInputQueue.sendInputEvent(q.mEvent, q, true, this);
return DEFER;
}
return FORWARD;
}
@Override
public void onFinishedInputEvent(Object token, boolean handled) {
QueuedInputEvent q = (QueuedInputEvent)token;
if (handled) {
finish(q, true);
return;
}
forward(q);
}
}
/**
@@ -3621,16 +3646,6 @@ public final class ViewRootImpl implements ViewParent,
finish(q, true);
return;
}
// If the window doesn't currently have input focus, then drop
// this event. This could be an event that came back from the
// IME dispatch but the window has lost focus in the meantime.
if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
finish(q, false);
return;
}
forward(q);
}
}
@@ -3702,15 +3717,30 @@ public final class ViewRootImpl implements ViewParent,
/**
* Delivers post-ime input events to a native activity.
*/
final class NativePostImeInputStage extends AsyncInputStage {
final class NativePostImeInputStage extends AsyncInputStage
implements InputQueue.FinishedInputEventCallback {
public NativePostImeInputStage(InputStage next, String traceCounter) {
super(next, traceCounter);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mInputQueue != null) {
mInputQueue.sendInputEvent(q.mEvent, q, false, this);
return DEFER;
}
return FORWARD;
}
@Override
public void onFinishedInputEvent(Object token, boolean handled) {
QueuedInputEvent q = (QueuedInputEvent)token;
if (handled) {
finish(q, true);
return;
}
forward(q);
}
}
/**

View File

@@ -46,6 +46,7 @@ LOCAL_SRC_FILES:= \
android_view_InputDevice.cpp \
android_view_InputEventReceiver.cpp \
android_view_InputEventSender.cpp \
android_view_InputQueue.cpp \
android_view_KeyEvent.cpp \
android_view_KeyCharacterMap.cpp \
android_view_HardwareRenderer.cpp \

View File

@@ -164,6 +164,7 @@ extern int register_android_view_InputChannel(JNIEnv* env);
extern int register_android_view_InputDevice(JNIEnv* env);
extern int register_android_view_InputEventReceiver(JNIEnv* env);
extern int register_android_view_InputEventSender(JNIEnv* env);
extern int register_android_view_InputQueue(JNIEnv* env);
extern int register_android_view_KeyCharacterMap(JNIEnv *env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
@@ -1197,6 +1198,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_view_InputChannel),
REG_JNI(register_android_view_InputEventReceiver),
REG_JNI(register_android_view_InputEventSender),
REG_JNI(register_android_view_InputQueue),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
REG_JNI(register_android_view_PointerIcon),

View File

@@ -45,8 +45,6 @@ namespace android
{
static struct {
jmethodID dispatchUnhandledKeyEvent;
jmethodID preDispatchKeyEvent;
jmethodID finish;
jmethodID setWindowFlags;
jmethodID setWindowFormat;
@@ -63,8 +61,7 @@ struct ActivityWork {
};
enum {
CMD_DEF_KEY = 1,
CMD_FINISH,
CMD_FINISH = 1,
CMD_SET_WINDOW_FORMAT,
CMD_SET_WINDOW_FLAGS,
CMD_SHOW_SOFT_INPUT,
@@ -101,299 +98,6 @@ static bool read_work(int fd, ActivityWork* outWork) {
return false;
}
// ------------------------------------------------------------------------
} // namespace android
using namespace android;
AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) :
mWorkWrite(workWrite), mConsumer(channel), mSeq(0) {
int msgpipe[2];
if (pipe(msgpipe)) {
ALOGW("could not create pipe: %s", strerror(errno));
mDispatchKeyRead = mDispatchKeyWrite = -1;
} else {
mDispatchKeyRead = msgpipe[0];
mDispatchKeyWrite = msgpipe[1];
int result = fcntl(mDispatchKeyRead, F_SETFL, O_NONBLOCK);
SLOGW_IF(result != 0, "Could not make AInputQueue read pipe "
"non-blocking: %s", strerror(errno));
result = fcntl(mDispatchKeyWrite, F_SETFL, O_NONBLOCK);
SLOGW_IF(result != 0, "Could not make AInputQueue write pipe "
"non-blocking: %s", strerror(errno));
}
}
AInputQueue::~AInputQueue() {
close(mDispatchKeyRead);
close(mDispatchKeyWrite);
}
void AInputQueue::attachLooper(ALooper* looper, int ident,
ALooper_callbackFunc callback, void* data) {
mLooper = static_cast<android::Looper*>(looper);
mLooper->addFd(mConsumer.getChannel()->getFd(),
ident, ALOOPER_EVENT_INPUT, callback, data);
mLooper->addFd(mDispatchKeyRead,
ident, ALOOPER_EVENT_INPUT, callback, data);
}
void AInputQueue::detachLooper() {
mLooper->removeFd(mConsumer.getChannel()->getFd());
mLooper->removeFd(mDispatchKeyRead);
}
int32_t AInputQueue::hasEvents() {
struct pollfd pfd[2];
pfd[0].fd = mConsumer.getChannel()->getFd();
pfd[0].events = POLLIN;
pfd[0].revents = 0;
pfd[1].fd = mDispatchKeyRead;
pfd[1].events = POLLIN;
pfd[1].revents = 0;
int nfd = poll(pfd, 2, 0);
if (nfd <= 0) return 0;
return ((pfd[0].revents & POLLIN) || (pfd[1].revents & POLLIN)) ? 1 : -1;
}
int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
*outEvent = NULL;
char byteread;
ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
Mutex::Autolock _l(mLock);
if (nRead == 1) {
if (mDispatchingKeys.size() > 0) {
KeyEvent* kevent = mDispatchingKeys[0];
*outEvent = kevent;
mDispatchingKeys.removeAt(0);
in_flight_event inflight;
inflight.event = kevent;
inflight.seq = -1;
inflight.finishSeq = 0;
mInFlightEvents.push(inflight);
}
bool finishNow = false;
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) {
ALOGW("getEvent couldn't find inflight for seq %d", finish.seq);
}
}
if (finishNow) {
finishEvent(*outEvent, true, false);
*outEvent = NULL;
return -1;
} else if (*outEvent != NULL) {
return 0;
}
}
uint32_t consumerSeq;
InputEvent* myEvent = NULL;
status_t res = mConsumer.consume(&mPooledInputEventFactory, true /*consumeBatches*/, -1,
&consumerSeq, &myEvent);
if (res != android::OK) {
if (res != android::WOULD_BLOCK) {
ALOGW("channel '%s' ~ Failed to consume input event. status=%d",
mConsumer.getChannel()->getName().string(), res);
}
return -1;
}
if (mConsumer.hasDeferredEvent()) {
wakeupDispatchLocked();
}
in_flight_event inflight;
inflight.event = myEvent;
inflight.seq = -1;
inflight.finishSeq = consumerSeq;
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 didDefaultHandling) {
LOG_TRACE("finishEvent: %p handled=%d, didDefaultHandling=%d", event,
handled ? 1 : 0, didDefaultHandling ? 1 : 0);
if (!handled && !didDefaultHandling
&& ((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.
doUnhandledKey((KeyEvent*)event);
return;
}
Mutex::Autolock _l(mLock);
const size_t N = mInFlightEvents.size();
for (size_t i=0; i<N; i++) {
const in_flight_event& inflight(mInFlightEvents[i]);
if (inflight.event == event) {
if (inflight.finishSeq) {
status_t res = mConsumer.sendFinishedSignal(inflight.finishSeq, handled);
if (res != android::OK) {
ALOGW("Failed to send finished signal on channel '%s'. status=%d",
mConsumer.getChannel()->getName().string(), res);
}
}
mPooledInputEventFactory.recycle(static_cast<InputEvent*>(event));
mInFlightEvents.removeAt(i);
return;
}
}
ALOGW("finishEvent called for unknown event: %p", event);
}
void AInputQueue::dispatchEvent(android::KeyEvent* event) {
Mutex::Autolock _l(mLock);
LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(),
mDispatchKeyWrite);
mDispatchingKeys.add(event);
wakeupDispatchLocked();
}
void AInputQueue::finishPreDispatch(int seq, bool handled) {
Mutex::Autolock _l(mLock);
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);
wakeupDispatchLocked();
}
KeyEvent* AInputQueue::consumeUnhandledEvent() {
Mutex::Autolock _l(mLock);
KeyEvent* event = NULL;
if (mUnhandledKeys.size() > 0) {
event = mUnhandledKeys[0];
mUnhandledKeys.removeAt(0);
}
LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event);
return event;
}
KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) {
Mutex::Autolock _l(mLock);
KeyEvent* event = NULL;
if (mPreDispatchingKeys.size() > 0) {
const in_flight_event& inflight(mPreDispatchingKeys[0]);
event = static_cast<KeyEvent*>(inflight.event);
*outSeq = inflight.seq;
mPreDispatchingKeys.removeAt(0);
}
LOG_TRACE("consumePreDispatchingEvent: KeyEvent=%p", event);
return event;
}
KeyEvent* AInputQueue::createKeyEvent() {
Mutex::Autolock _l(mLock);
return mPooledInputEventFactory.createKeyEvent();
}
void AInputQueue::doUnhandledKey(KeyEvent* keyEvent) {
Mutex::Autolock _l(mLock);
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);
}
bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
Mutex::Autolock _l(mLock);
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!");
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);
return true;
}
}
ALOGW("preDispatchKey called for unknown event: %p", keyEvent);
return false;
}
void AInputQueue::wakeupDispatchLocked() {
restart:
char dummy = 0;
int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
if (res < 0 && errno == EINTR) {
goto restart;
}
if (res == sizeof(dummy)) return;
if (res < 0) ALOGW("Failed writing to dispatch fd: %s", strerror(errno));
else ALOGW("Truncated writing to dispatch fd: %d", res);
}
namespace android {
// ------------------------------------------------------------------------
/*
* Native state for interacting with the NativeActivity class.
*/
@@ -404,8 +108,6 @@ struct NativeCode : public ANativeActivity {
dlhandle = _dlhandle;
createActivityFunc = _createFunc;
nativeWindow = NULL;
inputChannel = NULL;
nativeInputQueue = NULL;
mainWorkRead = mainWorkWrite = -1;
}
@@ -419,11 +121,7 @@ struct NativeCode : public ANativeActivity {
if (messageQueue != NULL && mainWorkRead >= 0) {
messageQueue->getLooper()->removeFd(mainWorkRead);
}
if (nativeInputQueue != NULL) {
nativeInputQueue->mWorkWrite = -1;
}
setSurface(NULL);
setInputChannel(NULL);
if (mainWorkRead >= 0) close(mainWorkRead);
if (mainWorkWrite >= 0) close(mainWorkWrite);
if (dlhandle != NULL) {
@@ -442,26 +140,6 @@ struct NativeCode : public ANativeActivity {
}
}
status_t setInputChannel(jobject _channel) {
if (inputChannel != NULL) {
delete nativeInputQueue;
env->DeleteGlobalRef(inputChannel);
}
inputChannel = NULL;
nativeInputQueue = NULL;
if (_channel != NULL) {
inputChannel = env->NewGlobalRef(_channel);
sp<InputChannel> ic =
android_view_InputChannel_getInputChannel(env, _channel);
if (ic != NULL) {
nativeInputQueue = new AInputQueue(ic, mainWorkWrite);
} else {
return UNKNOWN_ERROR;
}
}
return OK;
}
ANativeActivityCallbacks callbacks;
void* dlhandle;
@@ -475,9 +153,6 @@ struct NativeCode : public ANativeActivity {
int32_t lastWindowWidth;
int32_t lastWindowHeight;
jobject inputChannel;
struct AInputQueue* nativeInputQueue;
// These are used to wake up the main thread to process work.
int mainWorkRead;
int mainWorkWrite;
@@ -532,38 +207,6 @@ static int mainWorkCallback(int fd, int events, void* data) {
LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd);
switch (work.cmd) {
case CMD_DEF_KEY: {
KeyEvent* keyEvent;
while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) {
jobject inputEventObj = android_view_KeyEvent_fromNative(
code->env, keyEvent);
jboolean handled;
if (inputEventObj) {
handled = code->env->CallBooleanMethod(code->clazz,
gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
code->messageQueue->raiseAndClearException(
code->env, "dispatchUnhandledKeyEvent");
code->env->DeleteLocalRef(inputEventObj);
} else {
ALOGE("Failed to obtain key event for dispatchUnhandledKeyEvent.");
handled = false;
}
code->nativeInputQueue->finishEvent(keyEvent, handled, true);
}
int seq;
while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
jobject inputEventObj = android_view_KeyEvent_fromNative(
code->env, keyEvent);
if (inputEventObj) {
code->env->CallVoidMethod(code->clazz,
gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
code->messageQueue->raiseAndClearException(code->env, "preDispatchKeyEvent");
code->env->DeleteLocalRef(inputEventObj);
} else {
ALOGE("Failed to obtain key event for preDispatchKeyEvent.");
}
}
} break;
case CMD_FINISH: {
code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
code->messageQueue->raiseAndClearException(code->env, "finish");
@@ -903,36 +546,28 @@ onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surfa
}
static void
onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
onInputQueueCreated_native(JNIEnv* env, jobject clazz, jint handle, jint queuePtr)
{
LOG_TRACE("onInputChannelCreated_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
status_t err = code->setInputChannel(channel);
if (err != OK) {
jniThrowException(env, "java/lang/IllegalStateException",
"Error setting input channel");
return;
}
if (code->callbacks.onInputQueueCreated != NULL) {
code->callbacks.onInputQueueCreated(code,
code->nativeInputQueue);
AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr);
code->callbacks.onInputQueueCreated(code, queue);
}
}
}
static void
onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
onInputQueueDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jint queuePtr)
{
LOG_TRACE("onInputChannelDestroyed_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->nativeInputQueue != NULL
&& code->callbacks.onInputQueueDestroyed != NULL) {
code->callbacks.onInputQueueDestroyed(code,
code->nativeInputQueue);
if (code->callbacks.onInputQueueDestroyed != NULL) {
AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr);
code->callbacks.onInputQueueDestroyed(code, queue);
}
code->setInputChannel(NULL);
}
}
@@ -954,38 +589,6 @@ onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle,
}
}
static void
dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj)
{
LOG_TRACE("dispatchKeyEvent_native");
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->nativeInputQueue != NULL) {
KeyEvent* event = code->nativeInputQueue->createKeyEvent();
status_t status = android_view_KeyEvent_toNative(env, eventObj, event);
if (status) {
delete event;
jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
return;
}
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;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I",
(void*)loadNativeCode_native },
@@ -1002,11 +605,11 @@ static const JNINativeMethod g_methods[] = {
{ "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
{ "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
{ "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
{ "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native },
{ "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
{ "onInputQueueCreatedNative", "(II)V",
(void*)onInputQueueCreated_native },
{ "onInputQueueDestroyedNative", "(II)V",
(void*)onInputQueueDestroyed_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";
@@ -1025,13 +628,6 @@ int register_android_app_NativeActivity(JNIEnv* env)
jclass clazz;
FIND_CLASS(clazz, kNativeActivityPathName);
GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
clazz,
"dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)Z");
GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
clazz,
"preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
GET_METHOD_ID(gNativeActivityClassInfo.finish,
clazz,
"finish", "()V");

View File

@@ -0,0 +1,282 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "InputQueue"
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <android/input.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_InputQueue.h>
#include <androidfw/Input.h>
#include <utils/Looper.h>
#include <utils/TypeHelpers.h>
#include <ScopedLocalRef.h>
#include "JNIHelp.h"
#include "android_os_MessageQueue.h"
#include "android_view_KeyEvent.h"
#include "android_view_MotionEvent.h"
namespace android {
static struct {
jmethodID finishInputEvent;
} gInputQueueClassInfo;
enum {
MSG_FINISH_INPUT = 1,
};
InputQueue::InputQueue(jobject inputQueueObj, const sp<Looper>& looper,
int dispatchReadFd, int dispatchWriteFd) :
mDispatchReadFd(dispatchReadFd), mDispatchWriteFd(dispatchWriteFd),
mDispatchLooper(looper), mHandler(new WeakMessageHandler(this)) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
mInputQueueWeakGlobal = env->NewGlobalRef(inputQueueObj);
}
InputQueue::~InputQueue() {
mDispatchLooper->removeMessages(mHandler);
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mInputQueueWeakGlobal);
close(mDispatchReadFd);
close(mDispatchWriteFd);
}
void InputQueue::attachLooper(Looper* looper, int ident,
ALooper_callbackFunc callback, void* data) {
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mAppLoopers.size(); i++) {
if (looper == mAppLoopers[i]) {
return;
}
}
mAppLoopers.push(looper);
looper->addFd(mDispatchReadFd, ident, ALOOPER_EVENT_INPUT, callback, data);
}
void InputQueue::detachLooper() {
Mutex::Autolock _l(mLock);
detachLooperLocked();
}
void InputQueue::detachLooperLocked() {
for (size_t i = 0; i < mAppLoopers.size(); i++) {
mAppLoopers[i]->removeFd(mDispatchReadFd);
}
mAppLoopers.clear();
}
bool InputQueue::hasEvents() {
Mutex::Autolock _l(mLock);
return mPendingEvents.size() > 0;
}
status_t InputQueue::getEvent(InputEvent** outEvent) {
Mutex::Autolock _l(mLock);
*outEvent = NULL;
if (!mPendingEvents.isEmpty()) {
*outEvent = mPendingEvents[0];
mPendingEvents.removeAt(0);
}
if (mPendingEvents.isEmpty()) {
char byteread[16];
ssize_t nRead;
do {
nRead = TEMP_FAILURE_RETRY(read(mDispatchReadFd, &byteread, sizeof(byteread)));
if (nRead < 0 && errno != EAGAIN) {
ALOGW("Failed to read from native dispatch pipe: %s", strerror(errno));
}
} while (nRead > 0);
}
return *outEvent != NULL ? OK : WOULD_BLOCK;
}
bool InputQueue::preDispatchEvent(InputEvent* e) {
if (e->getType() == AINPUT_EVENT_TYPE_KEY) {
KeyEvent* keyEvent = static_cast<KeyEvent*>(e);
if (keyEvent->getFlags() & AKEY_EVENT_FLAG_PREDISPATCH) {
finishEvent(e, false);
return true;
}
}
return false;
}
void InputQueue::finishEvent(InputEvent* event, bool handled) {
Mutex::Autolock _l(mLock);
mFinishedEvents.push(key_value_pair_t<InputEvent*, bool>(event, handled));
if (mFinishedEvents.size() == 1) {
mDispatchLooper->sendMessage(this, Message(MSG_FINISH_INPUT));
}
}
void InputQueue::handleMessage(const Message& message) {
switch(message.what) {
case MSG_FINISH_INPUT:
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> inputQueueObj(env, jniGetReferent(env, mInputQueueWeakGlobal));
if (!inputQueueObj.get()) {
ALOGW("InputQueue was finalized without being disposed");
return;
}
while (true) {
InputEvent* event;
bool handled;
{
Mutex::Autolock _l(mLock);
if (mFinishedEvents.isEmpty()) {
break;
}
event = mFinishedEvents[0].getKey();
handled = mFinishedEvents[0].getValue();
mFinishedEvents.removeAt(0);
}
env->CallVoidMethod(inputQueueObj.get(), gInputQueueClassInfo.finishInputEvent,
reinterpret_cast<jint>(event), handled);
recycleInputEvent(event);
}
break;
}
}
void InputQueue::recycleInputEvent(InputEvent* event) {
mPooledInputEventFactory.recycle(event);
}
KeyEvent* InputQueue::createKeyEvent() {
return mPooledInputEventFactory.createKeyEvent();
}
MotionEvent* InputQueue::createMotionEvent() {
return mPooledInputEventFactory.createMotionEvent();
}
void InputQueue::enqueueEvent(InputEvent* event) {
Mutex::Autolock _l(mLock);
mPendingEvents.push(event);
if (mPendingEvents.size() == 1) {
char dummy = 0;
int res = TEMP_FAILURE_RETRY(write(mDispatchWriteFd, &dummy, sizeof(dummy)));
if (res < 0 && errno != EAGAIN) {
ALOGW("Failed writing to dispatch fd: %s", strerror(errno));
}
}
}
InputQueue* InputQueue::createQueue(jobject inputQueueObj, const sp<Looper>& looper) {
int pipeFds[2];
if (pipe(pipeFds)) {
ALOGW("Could not create native input dispatching pipe: %s", strerror(errno));
return NULL;
}
fcntl(pipeFds[0], F_SETFL, O_NONBLOCK);
fcntl(pipeFds[1], F_SETFL, O_NONBLOCK);
return new InputQueue(inputQueueObj, looper, pipeFds[0], pipeFds[1]);
}
static jint nativeInit(JNIEnv* env, jobject clazz, jobject queueWeak, jobject jMsgQueue) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, jMsgQueue);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
sp<InputQueue> queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper());
if (!queue.get()) {
jniThrowRuntimeException(env, "InputQueue failed to initialize");
return 0;
}
queue->incStrong(&gInputQueueClassInfo);
return reinterpret_cast<jint>(queue.get());
}
static void nativeDispose(JNIEnv* env, jobject clazz, jint ptr) {
sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
queue->detachLooper();
queue->decStrong(&gInputQueueClassInfo);
}
static jint nativeSendKeyEvent(JNIEnv* env, jobject clazz, jint ptr, jobject eventObj,
jboolean predispatch) {
InputQueue* queue = reinterpret_cast<InputQueue*>(ptr);
KeyEvent* event = queue->createKeyEvent();
status_t status = android_view_KeyEvent_toNative(env, eventObj, event);
if (status) {
queue->recycleInputEvent(event);
jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
return -1;
}
if (predispatch) {
event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH);
}
queue->enqueueEvent(event);
return reinterpret_cast<jint>(event);
}
static jint nativeSendMotionEvent(JNIEnv* env, jobject clazz, jint ptr, jobject eventObj) {
sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
MotionEvent* originalEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
if (!originalEvent) {
jniThrowRuntimeException(env, "Could not obtain MotionEvent pointer.");
return -1;
}
MotionEvent* event = queue->createMotionEvent();
event->copyFrom(originalEvent, true /* keepHistory */);
queue->enqueueEvent(event);
return reinterpret_cast<jint>(event);
}
static const JNINativeMethod g_methods[] = {
{ "nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)I",
(void*) nativeInit },
{ "nativeDispose", "(I)V", (void*) nativeDispose },
{ "nativeSendKeyEvent", "(ILandroid/view/KeyEvent;Z)I", (void*) nativeSendKeyEvent },
{ "nativeSendMotionEvent", "(ILandroid/view/MotionEvent;)I", (void*) nativeSendMotionEvent },
};
static const char* const kInputQueuePathName = "android/view/InputQueue";
#define FIND_CLASS(var, className) \
do { \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class %s", className); \
} while(0)
#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
do { \
var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method" methodName); \
} while(0)
int register_android_view_InputQueue(JNIEnv* env)
{
jclass clazz;
FIND_CLASS(clazz, kInputQueuePathName);
GET_METHOD_ID(gInputQueueClassInfo.finishInputEvent, clazz, "finishInputEvent", "(IZ)V");
return AndroidRuntime::registerNativeMethods(
env, kInputQueuePathName,
g_methods, NELEM(g_methods));
}
} // namespace android

View File

@@ -17,7 +17,6 @@
#ifndef _ANDROID_APP_NATIVEACTIVITY_H
#define _ANDROID_APP_NATIVEACTIVITY_H
#include <androidfw/InputTransport.h>
#include <utils/Looper.h>
#include <android/native_activity.h>
@@ -43,106 +42,4 @@ extern void android_NativeActivity_hideSoftInput(
} // namespace android
/*
* 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 {
public:
/* Creates a consumer associated with an input channel. */
explicit AInputQueue(const android::sp<android::InputChannel>& channel, int workWrite);
/* Destroys the consumer and releases its input channel. */
~AInputQueue();
void attachLooper(ALooper* looper, int ident, ALooper_callbackFunc callback, void* data);
void detachLooper();
int32_t hasEvents();
int32_t getEvent(AInputEvent** outEvent);
bool preDispatchEvent(AInputEvent* event);
void finishEvent(AInputEvent* event, bool handled, bool didDefaultHandling);
// ----------------------------------------------------------
inline android::InputConsumer& getConsumer() { return mConsumer; }
void dispatchEvent(android::KeyEvent* event);
void finishPreDispatch(int seq, bool handled);
android::KeyEvent* consumeUnhandledEvent();
android::KeyEvent* consumePreDispatchingEvent(int* outSeq);
android::KeyEvent* createKeyEvent();
int mWorkWrite;
private:
void doUnhandledKey(android::KeyEvent* keyEvent);
bool preDispatchKey(android::KeyEvent* keyEvent);
void wakeupDispatchLocked();
android::PooledInputEventFactory mPooledInputEventFactory;
android::InputConsumer mConsumer;
android::sp<android::Looper> mLooper;
int mDispatchKeyRead;
int mDispatchKeyWrite;
struct in_flight_event {
android::InputEvent* event;
int seq; // internal sequence number for synthetic pre-dispatch events
uint32_t finishSeq; // sequence number for sendFinishedSignal, or 0 if finish not required
};
struct finish_pre_dispatch {
int seq;
bool handled;
};
android::Mutex mLock;
int mSeq;
// 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

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _ANDROID_VIEW_INPUTQUEUE_H
#define _ANDROID_VIEW_INPUTQUEUE_H
#include <androidfw/Input.h>
#include <utils/Looper.h>
#include <utils/TypeHelpers.h>
#include <utils/Vector.h>
#include "JNIHelp.h"
/*
* Declare a concrete type for the NDK's AInputQueue forward declaration
*/
struct AInputQueue{
};
namespace android {
class InputQueue : public AInputQueue, public MessageHandler {
public:
virtual ~InputQueue();
void attachLooper(Looper* looper, int ident, ALooper_callbackFunc callback, void* data);
void detachLooper();
bool hasEvents();
status_t getEvent(InputEvent** outEvent);
bool preDispatchEvent(InputEvent* event);
void finishEvent(InputEvent* event, bool handled);
KeyEvent* createKeyEvent();
MotionEvent* createMotionEvent();
void recycleInputEvent(InputEvent* event);
void enqueueEvent(InputEvent* event);
static InputQueue* createQueue(jobject inputQueueObj, const sp<Looper>& looper);
protected:
virtual void handleMessage(const Message& message);
private:
InputQueue(jobject inputQueueObj, const sp<Looper>& looper,
int readDispatchFd, int writeDispatchFd);
void detachLooperLocked();
jobject mInputQueueWeakGlobal;
int mDispatchReadFd;
int mDispatchWriteFd;
Vector<Looper*> mAppLoopers;
sp<Looper> mDispatchLooper;
sp<WeakMessageHandler> mHandler;
PooledInputEventFactory mPooledInputEventFactory;
// Guards the pending and finished event vectors
mutable Mutex mLock;
Vector<InputEvent*> mPendingEvents;
Vector<key_value_pair_t<InputEvent*, bool> > mFinishedEvents;
};
} // namespace android
#endif

View File

@@ -36,6 +36,9 @@ class SkMatrix;
* Additional private constants not defined in ndk/ui/input.h.
*/
enum {
/* Signifies that the key is being predispatched */
AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000,
/* Private control to determine when an app is tracking a key sequence. */
AKEY_EVENT_FLAG_START_TRACKING = 0x40000000,

View File

@@ -25,12 +25,15 @@
#include <utils/Vector.h>
#include <android_runtime/android_app_NativeActivity.h>
#include <android_runtime/android_view_InputQueue.h>
#include <poll.h>
#include <errno.h>
using android::InputEvent;
using android::InputQueue;
using android::KeyEvent;
using android::Looper;
using android::MotionEvent;
using android::sp;
using android::Vector;
@@ -269,25 +272,37 @@ float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
int ident, ALooper_callbackFunc callback, void* data) {
queue->attachLooper(looper, ident, callback, data);
InputQueue* iq = static_cast<InputQueue*>(queue);
Looper* l = static_cast<Looper*>(looper);
iq->attachLooper(l, ident, callback, data);
}
void AInputQueue_detachLooper(AInputQueue* queue) {
queue->detachLooper();
InputQueue* iq = static_cast<InputQueue*>(queue);
iq->detachLooper();
}
int32_t AInputQueue_hasEvents(AInputQueue* queue) {
return queue->hasEvents();
InputQueue* iq = static_cast<InputQueue*>(queue);
return iq->hasEvents();
}
int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent) {
return queue->getEvent(outEvent);
InputQueue* iq = static_cast<InputQueue*>(queue);
InputEvent* event;
int32_t res = iq->getEvent(&event);
*outEvent = event;
return res;
}
int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event) {
return queue->preDispatchEvent(event) ? 1 : 0;
InputQueue* iq = static_cast<InputQueue*>(queue);
InputEvent* e = static_cast<InputEvent*>(event);
return iq->preDispatchEvent(e) ? 1 : 0;
}
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) {
queue->finishEvent(event, handled != 0, false);
InputQueue* iq = static_cast<InputQueue*>(queue);
InputEvent* e = static_cast<InputEvent*>(event);
iq->finishEvent(e, handled != 0);
}