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