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 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 event The input event that was received. 115 */ onInputEvent(InputEvent event)116 public void onInputEvent(InputEvent event) { 117 finishInputEvent(event, false); 118 } 119 120 /** 121 * Called when a batched input event is pending. 122 * 123 * The batched input event will continue to accumulate additional movement 124 * samples until the recipient calls {@link #consumeBatchedInputEvents} or 125 * an event is received that ends the batch and causes it to be consumed 126 * immediately (such as a pointer up event). 127 */ onBatchedInputEventPending()128 public void onBatchedInputEventPending() { 129 consumeBatchedInputEvents(-1); 130 } 131 132 /** 133 * Finishes an input event and indicates whether it was handled. 134 * Must be called on the same Looper thread to which the receiver is attached. 135 * 136 * @param event The input event that was finished. 137 * @param handled True if the event was handled. 138 */ finishInputEvent(InputEvent event, boolean handled)139 public final void finishInputEvent(InputEvent event, boolean handled) { 140 if (event == null) { 141 throw new IllegalArgumentException("event must not be null"); 142 } 143 if (mReceiverPtr == 0) { 144 Log.w(TAG, "Attempted to finish an input event but the input event " 145 + "receiver has already been disposed."); 146 } else { 147 int index = mSeqMap.indexOfKey(event.getSequenceNumber()); 148 if (index < 0) { 149 Log.w(TAG, "Attempted to finish an input event that is not in progress."); 150 } else { 151 int seq = mSeqMap.valueAt(index); 152 mSeqMap.removeAt(index); 153 nativeFinishInputEvent(mReceiverPtr, seq, handled); 154 } 155 } 156 event.recycleIfNeededAfterDispatch(); 157 } 158 159 /** 160 * Consumes all pending batched input events. 161 * Must be called on the same Looper thread to which the receiver is attached. 162 * 163 * This method forces all batched input events to be delivered immediately. 164 * Should be called just before animating or drawing a new frame in the UI. 165 * 166 * @param frameTimeNanos The time in the {@link System#nanoTime()} time base 167 * when the current display frame started rendering, or -1 if unknown. 168 * 169 * @return Whether a batch was consumed 170 */ consumeBatchedInputEvents(long frameTimeNanos)171 public final boolean consumeBatchedInputEvents(long frameTimeNanos) { 172 if (mReceiverPtr == 0) { 173 Log.w(TAG, "Attempted to consume batched input events but the input event " 174 + "receiver has already been disposed."); 175 } else { 176 return nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos); 177 } 178 return false; 179 } 180 181 // Called from native code. 182 @SuppressWarnings("unused") dispatchInputEvent(int seq, InputEvent event)183 private void dispatchInputEvent(int seq, InputEvent event) { 184 mSeqMap.put(event.getSequenceNumber(), seq); 185 onInputEvent(event); 186 } 187 188 // Called from native code. 189 @SuppressWarnings("unused") dispatchBatchedInputEventPending()190 private void dispatchBatchedInputEventPending() { 191 onBatchedInputEventPending(); 192 } 193 194 public static interface Factory { createInputEventReceiver( InputChannel inputChannel, Looper looper)195 public InputEventReceiver createInputEventReceiver( 196 InputChannel inputChannel, Looper looper); 197 } 198 } 199