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