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