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

Commit a44dd26a authored by Michael Wright's avatar Michael Wright
Browse files

Rewrite input handling for native applications

Bug: 8473020
Change-Id: Ic4353d8924ab877bec21aff8c2dba9fe725bf906
parent c3d0a81a
Loading
Loading
Loading
Loading
+6 −40
Original line number Original line Diff line number Diff line
@@ -27,7 +27,6 @@ import android.os.Environment;
import android.os.Looper;
import android.os.Looper;
import android.os.MessageQueue;
import android.os.MessageQueue;
import android.util.AttributeSet;
import android.util.AttributeSet;
import android.view.InputChannel;
import android.view.InputQueue;
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.KeyEvent;
import android.view.Surface;
import android.view.Surface;
@@ -111,11 +110,9 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
            int format, int width, int height);
            int format, int width, int height);
    private native void onSurfaceRedrawNeededNative(int handle, Surface surface);
    private native void onSurfaceRedrawNeededNative(int handle, Surface surface);
    private native void onSurfaceDestroyedNative(int handle);
    private native void onSurfaceDestroyedNative(int handle);
    private native void onInputChannelCreatedNative(int handle, InputChannel channel);
    private native void onInputQueueCreatedNative(int handle, int queuePtr);
    private native void onInputChannelDestroyedNative(int handle, InputChannel channel);
    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 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 {
    static class NativeContentView extends View {
        NativeActivity mActivity;
        NativeActivity mActivity;
@@ -197,7 +194,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
            mCurSurfaceHolder = null;
            mCurSurfaceHolder = null;
        }
        }
        if (mCurInputQueue != null) {
        if (mCurInputQueue != null) {
            onInputChannelDestroyedNative(mNativeHandle, mCurInputQueue.getInputChannel());
            onInputQueueDestroyedNative(mNativeHandle, mCurInputQueue.getNativePtr());
            mCurInputQueue = null;
            mCurInputQueue = null;
        }
        }
        unloadNativeCode(mNativeHandle);
        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) {
    public void surfaceCreated(SurfaceHolder holder) {
        if (!mDestroyed) {
        if (!mDestroyed) {
            mCurSurfaceHolder = holder;
            mCurSurfaceHolder = holder;
@@ -304,14 +289,14 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
    public void onInputQueueCreated(InputQueue queue) {
    public void onInputQueueCreated(InputQueue queue) {
        if (!mDestroyed) {
        if (!mDestroyed) {
            mCurInputQueue = queue;
            mCurInputQueue = queue;
            onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel());
            onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr());
        }
        }
    }
    }
    
    
    public void onInputQueueDestroyed(InputQueue queue) {
    public void onInputQueueDestroyed(InputQueue queue) {
        mCurInputQueue = null;
        if (!mDestroyed) {
        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) {
    void setWindowFlags(int flags, int mask) {
        getWindow().setFlags(flags, mask);
        getWindow().setFlags(flags, mask);
    }
    }
+120 −10
Original line number Original line Diff line number Diff line
@@ -16,11 +16,127 @@


package android.view;
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
 * An input queue provides a mechanism for an application to receive incoming
 * input events.  Currently only usable from native code.
 * input events.  Currently only usable from native code.
 */
 */
public final class InputQueue {
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
     * Interface to receive notification of when an InputQueue is associated
     * and dissociated with a thread.
     * and dissociated with a thread.
@@ -39,15 +155,9 @@ public final class InputQueue {
        void onInputQueueDestroyed(InputQueue queue);
        void onInputQueueDestroyed(InputQueue queue);
    }
    }


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


    /** @hide */
    public InputChannel getInputChannel() {
        return mChannel;
    }
}
}
+6 −0
Original line number Original line Diff line number Diff line
@@ -1232,6 +1232,12 @@ public class KeyEvent extends InputEvent implements Parcelable {
     */
     */
    public static final int FLAG_FALLBACK = 0x400;
    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.
     * Private control to determine when an app is tracking a key sequence.
     * @hide
     * @hide
+47 −17
Original line number Original line Diff line number Diff line
@@ -597,13 +597,12 @@ public final class ViewRootImpl implements ViewParent,
                }
                }
                if (mInputChannel != null) {
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue(mInputChannel);
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    } else {
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                            Looper.myLooper());
                }
                }
                }


                view.assignParent(this);
                view.assignParent(this);
                mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
                mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
@@ -2822,9 +2821,11 @@ public final class ViewRootImpl implements ViewParent,


        if (mInputQueueCallback != null && mInputQueue != null) {
        if (mInputQueueCallback != null && mInputQueue != null) {
            mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
            mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
            mInputQueue.dispose();
            mInputQueueCallback = null;
            mInputQueueCallback = null;
            mInputQueue = null;
            mInputQueue = null;
        } else if (mInputEventReceiver != null) {
        }
        if (mInputEventReceiver != null) {
            mInputEventReceiver.dispose();
            mInputEventReceiver.dispose();
            mInputEventReceiver = null;
            mInputEventReceiver = null;
        }
        }
@@ -3347,6 +3348,15 @@ public final class ViewRootImpl implements ViewParent,
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);
                forward(q);
            } else if (mView == null || !mAdded) {
            } 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);
                finish(q, false);
            } else {
            } else {
                apply(q, onProcess(q));
                apply(q, onProcess(q));
@@ -3547,15 +3557,30 @@ public final class ViewRootImpl implements ViewParent,
     * Delivers pre-ime input events to a native activity.
     * Delivers pre-ime input events to a native activity.
     * Does not support pointer events.
     * Does not support pointer events.
     */
     */
    final class NativePreImeInputStage extends AsyncInputStage {
    final class NativePreImeInputStage extends AsyncInputStage
            implements InputQueue.FinishedInputEventCallback {
        public NativePreImeInputStage(InputStage next, String traceCounter) {
        public NativePreImeInputStage(InputStage next, String traceCounter) {
            super(next, traceCounter);
            super(next, traceCounter);
        }
        }


        @Override
        @Override
        protected int onProcess(QueuedInputEvent q) {
        protected int onProcess(QueuedInputEvent q) {
            if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
                mInputQueue.sendInputEvent(q.mEvent, q, true, this);
                return DEFER;
            }
            return FORWARD;
            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);
                finish(q, true);
                return;
                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);
            forward(q);
        }
        }
    }
    }
@@ -3702,15 +3717,30 @@ public final class ViewRootImpl implements ViewParent,
    /**
    /**
     * Delivers post-ime input events to a native activity.
     * 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) {
        public NativePostImeInputStage(InputStage next, String traceCounter) {
            super(next, traceCounter);
            super(next, traceCounter);
        }
        }


        @Override
        @Override
        protected int onProcess(QueuedInputEvent q) {
        protected int onProcess(QueuedInputEvent q) {
            if (mInputQueue != null) {
                mInputQueue.sendInputEvent(q.mEvent, q, false, this);
                return DEFER;
            }
            return FORWARD;
            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 Original line Diff line number Diff line
@@ -46,6 +46,7 @@ LOCAL_SRC_FILES:= \
	android_view_InputDevice.cpp \
	android_view_InputDevice.cpp \
	android_view_InputEventReceiver.cpp \
	android_view_InputEventReceiver.cpp \
	android_view_InputEventSender.cpp \
	android_view_InputEventSender.cpp \
	android_view_InputQueue.cpp \
	android_view_KeyEvent.cpp \
	android_view_KeyEvent.cpp \
	android_view_KeyCharacterMap.cpp \
	android_view_KeyCharacterMap.cpp \
	android_view_HardwareRenderer.cpp \
	android_view_HardwareRenderer.cpp \
Loading