Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5fe6e4c4 authored by Michael Wright's avatar Michael Wright Committed by Android (Google) Code Review
Browse files

Merge "Rewrite input handling for native applications" into jb-mr2-dev

parents a61464d7 a44dd26a
Loading
Loading
Loading
Loading
+6 −40
Original line number Diff line number Diff line
@@ -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);
    }
+120 −10
Original line number Diff line number Diff line
@@ -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.
@@ -39,15 +155,9 @@ public final class InputQueue {
        void onInputQueueDestroyed(InputQueue queue);
    }

    final InputChannel mChannel;
    
    /** @hide */
    public InputQueue(InputChannel channel) {
        mChannel = channel;
    public static interface FinishedInputEventCallback {
        void onFinishedInputEvent(Object token, boolean handled);
    }

    /** @hide */
    public InputChannel getInputChannel() {
        return mChannel;
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -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
+47 −17
Original line number Diff line number Diff line
@@ -597,13 +597,12 @@ 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());
                }
                }

                view.assignParent(this);
                mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
@@ -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);
        }
    }

    /**
+1 −0
Original line number Diff line number Diff line
@@ -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 \
Loading