1/*
2 * Copyright (C) 2013 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 dalvik.system.CloseGuard;
20
21import android.os.Looper;
22import android.os.MessageQueue;
23import android.util.Log;
24
25import java.lang.ref.WeakReference;
26
27/**
28 * Provides a low-level mechanism for an application to send input events.
29 * @hide
30 */
31public abstract class InputEventSender {
32    private static final String TAG = "InputEventSender";
33
34    private final CloseGuard mCloseGuard = CloseGuard.get();
35
36    private long mSenderPtr;
37
38    // We keep references to the input channel and message queue objects here so that
39    // they are not GC'd while the native peer of the receiver is using them.
40    private InputChannel mInputChannel;
41    private MessageQueue mMessageQueue;
42
43    private static native long nativeInit(WeakReference<InputEventSender> sender,
44            InputChannel inputChannel, MessageQueue messageQueue);
45    private static native void nativeDispose(long senderPtr);
46    private static native boolean nativeSendKeyEvent(long senderPtr, int seq, KeyEvent event);
47    private static native boolean nativeSendMotionEvent(long senderPtr, int seq, MotionEvent event);
48
49    /**
50     * Creates an input event sender bound to the specified input channel.
51     *
52     * @param inputChannel The input channel.
53     * @param looper The looper to use when invoking callbacks.
54     */
55    public InputEventSender(InputChannel inputChannel, Looper looper) {
56        if (inputChannel == null) {
57            throw new IllegalArgumentException("inputChannel must not be null");
58        }
59        if (looper == null) {
60            throw new IllegalArgumentException("looper must not be null");
61        }
62
63        mInputChannel = inputChannel;
64        mMessageQueue = looper.getQueue();
65        mSenderPtr = nativeInit(new WeakReference<InputEventSender>(this),
66                inputChannel, mMessageQueue);
67
68        mCloseGuard.open("dispose");
69    }
70
71    @Override
72    protected void finalize() throws Throwable {
73        try {
74            dispose(true);
75        } finally {
76            super.finalize();
77        }
78    }
79
80    /**
81     * Disposes the receiver.
82     */
83    public void dispose() {
84        dispose(false);
85    }
86
87    private void dispose(boolean finalized) {
88        if (mCloseGuard != null) {
89            if (finalized) {
90                mCloseGuard.warnIfOpen();
91            }
92            mCloseGuard.close();
93        }
94
95        if (mSenderPtr != 0) {
96            nativeDispose(mSenderPtr);
97            mSenderPtr = 0;
98        }
99        mInputChannel = null;
100        mMessageQueue = null;
101    }
102
103    /**
104     * Called when an input event is finished.
105     *
106     * @param seq The input event sequence number.
107     * @param handled True if the input event was handled.
108     */
109    public void onInputEventFinished(int seq, boolean handled) {
110    }
111
112    /**
113     * Sends an input event.
114     * Must be called on the same Looper thread to which the sender is attached.
115     *
116     * @param seq The input event sequence number.
117     * @param event The input event to send.
118     * @return True if the entire event was sent successfully.  May return false
119     * if the input channel buffer filled before all samples were dispatched.
120     */
121    public final boolean sendInputEvent(int seq, InputEvent event) {
122        if (event == null) {
123            throw new IllegalArgumentException("event must not be null");
124        }
125        if (mSenderPtr == 0) {
126            Log.w(TAG, "Attempted to send an input event but the input event "
127                    + "sender has already been disposed.");
128            return false;
129        }
130
131        if (event instanceof KeyEvent) {
132            return nativeSendKeyEvent(mSenderPtr, seq, (KeyEvent)event);
133        } else {
134            return nativeSendMotionEvent(mSenderPtr, seq, (MotionEvent)event);
135        }
136    }
137
138    // Called from native code.
139    @SuppressWarnings("unused")
140    private void dispatchInputEventFinished(int seq, boolean handled) {
141        onInputEventFinished(seq, handled);
142    }
143}
144