1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
19import android.os.Looper;
20import android.os.MessageQueue;
21import android.util.LongSparseArray;
22import android.util.Pools.Pool;
23import android.util.Pools.SimplePool;
24
25import dalvik.system.CloseGuard;
26
27import java.lang.ref.WeakReference;
28
29/**
30 * An input queue provides a mechanism for an application to receive incoming
31 * input events.  Currently only usable from native code.
32 */
33public final class InputQueue {
34    private final LongSparseArray<ActiveInputEvent> mActiveEventArray =
35            new LongSparseArray<ActiveInputEvent>(20);
36    private final Pool<ActiveInputEvent> mActiveInputEventPool =
37            new SimplePool<ActiveInputEvent>(20);
38
39    private final CloseGuard mCloseGuard = CloseGuard.get();
40
41    private long mPtr;
42
43    private static native long nativeInit(WeakReference<InputQueue> weakQueue,
44            MessageQueue messageQueue);
45    private static native long nativeSendKeyEvent(long ptr, KeyEvent e, boolean preDispatch);
46    private static native long nativeSendMotionEvent(long ptr, MotionEvent e);
47    private static native void nativeDispose(long ptr);
48
49    /** @hide */
50    public InputQueue() {
51        mPtr = nativeInit(new WeakReference<InputQueue>(this), Looper.myQueue());
52
53        mCloseGuard.open("dispose");
54    }
55
56    @Override
57    protected void finalize() throws Throwable {
58        try {
59            dispose(true);
60        } finally {
61            super.finalize();
62        }
63    }
64
65    /** @hide */
66    public void dispose() {
67        dispose(false);
68    }
69
70    /** @hide */
71    public void dispose(boolean finalized) {
72        if (mCloseGuard != null) {
73            if (finalized) {
74                mCloseGuard.warnIfOpen();
75            }
76            mCloseGuard.close();
77        }
78
79        if (mPtr != 0) {
80            nativeDispose(mPtr);
81            mPtr = 0;
82        }
83    }
84
85    /** @hide */
86    public long getNativePtr() {
87        return mPtr;
88    }
89
90    /** @hide */
91    public void sendInputEvent(InputEvent e, Object token, boolean predispatch,
92            FinishedInputEventCallback callback) {
93        ActiveInputEvent event = obtainActiveInputEvent(token, callback);
94        long id;
95        if (e instanceof KeyEvent) {
96            id = nativeSendKeyEvent(mPtr, (KeyEvent) e, predispatch);
97        } else {
98            id = nativeSendMotionEvent(mPtr, (MotionEvent) e);
99        }
100        mActiveEventArray.put(id, event);
101    }
102
103    private void finishInputEvent(long id, boolean handled) {
104        int index = mActiveEventArray.indexOfKey(id);
105        if (index >= 0) {
106            ActiveInputEvent e = mActiveEventArray.valueAt(index);
107            mActiveEventArray.removeAt(index);
108            e.mCallback.onFinishedInputEvent(e.mToken, handled);
109            recycleActiveInputEvent(e);
110        }
111    }
112
113    private ActiveInputEvent obtainActiveInputEvent(Object token,
114            FinishedInputEventCallback callback) {
115        ActiveInputEvent e = mActiveInputEventPool.acquire();
116        if (e == null) {
117            e = new ActiveInputEvent();
118        }
119        e.mToken = token;
120        e.mCallback = callback;
121        return e;
122    }
123
124    private void recycleActiveInputEvent(ActiveInputEvent e) {
125        e.recycle();
126        mActiveInputEventPool.release(e);
127    }
128
129    private final class ActiveInputEvent {
130        public Object mToken;
131        public FinishedInputEventCallback mCallback;
132
133        public void recycle() {
134            mToken = null;
135            mCallback = null;
136        }
137    }
138
139    /**
140     * Interface to receive notification of when an InputQueue is associated
141     * and dissociated with a thread.
142     */
143    public static interface Callback {
144        /**
145         * Called when the given InputQueue is now associated with the
146         * thread making this call, so it can start receiving events from it.
147         */
148        void onInputQueueCreated(InputQueue queue);
149
150        /**
151         * Called when the given InputQueue is no longer associated with
152         * the thread and thus not dispatching events.
153         */
154        void onInputQueueDestroyed(InputQueue queue);
155    }
156
157    /** @hide */
158    public static interface FinishedInputEventCallback {
159        void onFinishedInputEvent(Object token, boolean handled);
160    }
161
162}
163