• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 
17 package android.view;
18 
19 import dalvik.system.CloseGuard;
20 
21 import android.os.Looper;
22 import android.os.MessageQueue;
23 import android.util.Log;
24 import android.util.SparseIntArray;
25 
26 /**
27  * Provides a low-level mechanism for an application to receive input events.
28  * @hide
29  */
30 public abstract class InputEventReceiver {
31     private static final String TAG = "InputEventReceiver";
32 
33     private final CloseGuard mCloseGuard = CloseGuard.get();
34 
35     private int mReceiverPtr;
36 
37     // We keep references to the input channel and message queue objects here so that
38     // they are not GC'd while the native peer of the receiver is using them.
39     private InputChannel mInputChannel;
40     private MessageQueue mMessageQueue;
41 
42     // Map from InputEvent sequence numbers to dispatcher sequence numbers.
43     private final SparseIntArray mSeqMap = new SparseIntArray();
44 
nativeInit(InputEventReceiver receiver, InputChannel inputChannel, MessageQueue messageQueue)45     private static native int nativeInit(InputEventReceiver receiver,
46             InputChannel inputChannel, MessageQueue messageQueue);
nativeDispose(int receiverPtr)47     private static native void nativeDispose(int receiverPtr);
nativeFinishInputEvent(int receiverPtr, int seq, boolean handled)48     private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled);
nativeConsumeBatchedInputEvents(int receiverPtr, long frameTimeNanos)49     private static native void nativeConsumeBatchedInputEvents(int receiverPtr,
50             long frameTimeNanos);
51 
52     /**
53      * Creates an input event receiver bound to the specified input channel.
54      *
55      * @param inputChannel The input channel.
56      * @param looper The looper to use when invoking callbacks.
57      */
InputEventReceiver(InputChannel inputChannel, Looper looper)58     public InputEventReceiver(InputChannel inputChannel, Looper looper) {
59         if (inputChannel == null) {
60             throw new IllegalArgumentException("inputChannel must not be null");
61         }
62         if (looper == null) {
63             throw new IllegalArgumentException("looper must not be null");
64         }
65 
66         mInputChannel = inputChannel;
67         mMessageQueue = looper.getQueue();
68         mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue);
69 
70         mCloseGuard.open("dispose");
71     }
72 
73     @Override
finalize()74     protected void finalize() throws Throwable {
75         try {
76             dispose(true);
77         } finally {
78             super.finalize();
79         }
80     }
81 
82     /**
83      * Disposes the receiver.
84      */
dispose()85     public void dispose() {
86         dispose(false);
87     }
88 
dispose(boolean finalized)89     private void dispose(boolean finalized) {
90         if (mCloseGuard != null) {
91             if (finalized) {
92                 mCloseGuard.warnIfOpen();
93             }
94             mCloseGuard.close();
95         }
96 
97         if (mReceiverPtr != 0) {
98             nativeDispose(mReceiverPtr);
99             mReceiverPtr = 0;
100         }
101         mInputChannel = null;
102         mMessageQueue = null;
103     }
104 
105     /**
106      * Called when an input event is received.
107      * The recipient should process the input event and then call {@link #finishInputEvent}
108      * to indicate whether the event was handled.  No new input events will be received
109      * until {@link #finishInputEvent} is called.
110      *
111      * @param event The input event that was received.
112      */
onInputEvent(InputEvent event)113     public void onInputEvent(InputEvent event) {
114         finishInputEvent(event, false);
115     }
116 
117     /**
118      * Called when a batched input event is pending.
119      *
120      * The batched input event will continue to accumulate additional movement
121      * samples until the recipient calls {@link #consumeBatchedInputEvents} or
122      * an event is received that ends the batch and causes it to be consumed
123      * immediately (such as a pointer up event).
124      */
onBatchedInputEventPending()125     public void onBatchedInputEventPending() {
126         consumeBatchedInputEvents(-1);
127     }
128 
129     /**
130      * Finishes an input event and indicates whether it was handled.
131      * Must be called on the same Looper thread to which the receiver is attached.
132      *
133      * @param event The input event that was finished.
134      * @param handled True if the event was handled.
135      */
finishInputEvent(InputEvent event, boolean handled)136     public final void finishInputEvent(InputEvent event, boolean handled) {
137         if (event == null) {
138             throw new IllegalArgumentException("event must not be null");
139         }
140         if (mReceiverPtr == 0) {
141             Log.w(TAG, "Attempted to finish an input event but the input event "
142                     + "receiver has already been disposed.");
143         } else {
144             int index = mSeqMap.indexOfKey(event.getSequenceNumber());
145             if (index < 0) {
146                 Log.w(TAG, "Attempted to finish an input event that is not in progress.");
147             } else {
148                 int seq = mSeqMap.valueAt(index);
149                 mSeqMap.removeAt(index);
150                 nativeFinishInputEvent(mReceiverPtr, seq, handled);
151             }
152         }
153         event.recycleIfNeededAfterDispatch();
154     }
155 
156     /**
157      * Consumes all pending batched input events.
158      * Must be called on the same Looper thread to which the receiver is attached.
159      *
160      * This method forces all batched input events to be delivered immediately.
161      * Should be called just before animating or drawing a new frame in the UI.
162      *
163      * @param frameTimeNanos The time in the {@link System#nanoTime()} time base
164      * when the current display frame started rendering, or -1 if unknown.
165      */
consumeBatchedInputEvents(long frameTimeNanos)166     public final void consumeBatchedInputEvents(long frameTimeNanos) {
167         if (mReceiverPtr == 0) {
168             Log.w(TAG, "Attempted to consume batched input events but the input event "
169                     + "receiver has already been disposed.");
170         } else {
171             nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos);
172         }
173     }
174 
175     // Called from native code.
176     @SuppressWarnings("unused")
dispatchInputEvent(int seq, InputEvent event)177     private void dispatchInputEvent(int seq, InputEvent event) {
178         mSeqMap.put(event.getSequenceNumber(), seq);
179         onInputEvent(event);
180     }
181 
182     // Called from native code.
183     @SuppressWarnings("unused")
dispatchBatchedInputEventPending()184     private void dispatchBatchedInputEventPending() {
185         onBatchedInputEventPending();
186     }
187 
188     public static interface Factory {
createInputEventReceiver( InputChannel inputChannel, Looper looper)189         public InputEventReceiver createInputEventReceiver(
190                 InputChannel inputChannel, Looper looper);
191     }
192 }
193