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