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 45 private static native int nativeInit(InputEventReceiver receiver, 46 InputChannel inputChannel, MessageQueue messageQueue); 47 private static native void nativeDispose(int receiverPtr); 48 private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled); 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 */ 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 74 protected void finalize() throws Throwable { 75 try { 76 dispose(); 77 } finally { 78 super.finalize(); 79 } 80 } 81 82 /** 83 * Disposes the receiver. 84 */ 85 public void dispose() { 86 if (mCloseGuard != null) { 87 mCloseGuard.close(); 88 } 89 if (mReceiverPtr != 0) { 90 nativeDispose(mReceiverPtr); 91 mReceiverPtr = 0; 92 } 93 mInputChannel = null; 94 mMessageQueue = null; 95 } 96 97 /** 98 * Called when an input event is received. 99 * The recipient should process the input event and then call {@link #finishInputEvent} 100 * to indicate whether the event was handled. No new input events will be received 101 * until {@link #finishInputEvent} is called. 102 * 103 * @param event The input event that was received. 104 */ 105 public void onInputEvent(InputEvent event) { 106 finishInputEvent(event, false); 107 } 108 109 /** 110 * Called when a batched input event is pending. 111 * 112 * The batched input event will continue to accumulate additional movement 113 * samples until the recipient calls {@link #consumeBatchedInputEvents} or 114 * an event is received that ends the batch and causes it to be consumed 115 * immediately (such as a pointer up event). 116 */ 117 public void onBatchedInputEventPending() { 118 consumeBatchedInputEvents(-1); 119 } 120 121 /** 122 * Finishes an input event and indicates whether it was handled. 123 * Must be called on the same Looper thread to which the receiver is attached. 124 * 125 * @param event The input event that was finished. 126 * @param handled True if the event was handled. 127 */ 128 public final void finishInputEvent(InputEvent event, boolean handled) { 129 if (event == null) { 130 throw new IllegalArgumentException("event must not be null"); 131 } 132 if (mReceiverPtr == 0) { 133 Log.w(TAG, "Attempted to finish an input event but the input event " 134 + "receiver has already been disposed."); 135 } else { 136 int index = mSeqMap.indexOfKey(event.getSequenceNumber()); 137 if (index < 0) { 138 Log.w(TAG, "Attempted to finish an input event that is not in progress."); 139 } else { 140 int seq = mSeqMap.valueAt(index); 141 mSeqMap.removeAt(index); 142 nativeFinishInputEvent(mReceiverPtr, seq, handled); 143 } 144 } 145 event.recycleIfNeededAfterDispatch(); 146 } 147 148 /** 149 * Consumes all pending batched input events. 150 * Must be called on the same Looper thread to which the receiver is attached. 151 * 152 * This method forces all batched input events to be delivered immediately. 153 * Should be called just before animating or drawing a new frame in the UI. 154 * 155 * @param frameTimeNanos The time in the {@link System#nanoTime()} time base 156 * when the current display frame started rendering, or -1 if unknown. 157 */ 158 public final void consumeBatchedInputEvents(long frameTimeNanos) { 159 if (mReceiverPtr == 0) { 160 Log.w(TAG, "Attempted to consume batched input events but the input event " 161 + "receiver has already been disposed."); 162 } else { 163 nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos); 164 } 165 } 166 167 // Called from native code. 168 @SuppressWarnings("unused") 169 private void dispatchInputEvent(int seq, InputEvent event) { 170 mSeqMap.put(event.getSequenceNumber(), seq); 171 onInputEvent(event); 172 } 173 174 // Called from native code. 175 @SuppressWarnings("unused") 176 private void dispatchBatchedInputEventPending() { 177 onBatchedInputEventPending(); 178 } 179 180 public static interface Factory { 181 public InputEventReceiver createInputEventReceiver( 182 InputChannel inputChannel, Looper looper); 183 } 184 } 185