Skip to content

Commit

Permalink
Rewrite input handling for native applications
Browse files Browse the repository at this point in the history
Bug: 8473020
Change-Id: Ic4353d8924ab877bec21aff8c2dba9fe725bf906
  • Loading branch information
Michael Wright committed Apr 23, 2013
1 parent c3d0a81 commit a44dd26
Show file tree
Hide file tree
Showing 12 changed files with 585 additions and 592 deletions.
46 changes: 6 additions & 40 deletions core/java/android/app/NativeActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
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;
Expand Down Expand Up @@ -111,11 +110,9 @@ private native void onSurfaceChangedNative(int handle, Surface surface,
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;
Expand Down Expand Up @@ -197,7 +194,7 @@ protected void onDestroy() {
mCurSurfaceHolder = null;
}
if (mCurInputQueue != null) {
onInputChannelDestroyedNative(mNativeHandle, mCurInputQueue.getInputChannel());
onInputQueueDestroyedNative(mNativeHandle, mCurInputQueue.getNativePtr());
mCurInputQueue = null;
}
unloadNativeCode(mNativeHandle);
Expand Down Expand Up @@ -261,18 +258,6 @@ public void onWindowFocusChanged(boolean hasFocus) {
}
}

@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;
Expand Down Expand Up @@ -304,14 +289,14 @@ public void surfaceDestroyed(SurfaceHolder holder) {
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;
}
}

Expand All @@ -332,25 +317,6 @@ public void onGlobalLayout() {
}
}

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);
}
Expand Down
130 changes: 120 additions & 10 deletions core/java/android/view/InputQueue.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -31,23 +147,17 @@ public static interface Callback {
* 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.
*/
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);
}

}
6 changes: 6 additions & 0 deletions core/java/android/view/KeyEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,12 @@ private static void populateKeycodeSymbolicNames() {
*/
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
Expand Down
64 changes: 47 additions & 17 deletions core/java/android/view/ViewRootImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -597,12 +597,11 @@ public void setView(View view, WindowManager.LayoutParams attrs, View panelParen
}
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);
Expand Down Expand Up @@ -2822,9 +2821,11 @@ void dispatchDetachedFromWindow() {

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;
}
Expand Down Expand Up @@ -3347,6 +3348,15 @@ public final void deliver(QueuedInputEvent q) {
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));
Expand Down Expand Up @@ -3547,15 +3557,30 @@ private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) {
* 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);
}
}

/**
Expand Down Expand Up @@ -3621,16 +3646,6 @@ public void onFinishedInputEvent(Object token, boolean handled) {
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);
}
}
Expand Down Expand Up @@ -3702,15 +3717,30 @@ private int processPointerEvent(QueuedInputEvent q) {
/**
* 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);
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions core/jni/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
Loading

0 comments on commit a44dd26

Please sign in to comment.