1 /* 2 * Copyright (C) 2010 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.os.MessageQueue; 20 import android.util.Slog; 21 22 /** 23 * An input queue provides a mechanism for an application to receive incoming 24 * input events. Currently only usable from native code. 25 */ 26 public final class InputQueue { 27 private static final String TAG = "InputQueue"; 28 29 private static final boolean DEBUG = false; 30 31 /** 32 * Interface to receive notification of when an InputQueue is associated 33 * and dissociated with a thread. 34 */ 35 public static interface Callback { 36 /** 37 * Called when the given InputQueue is now associated with the 38 * thread making this call, so it can start receiving events from it. 39 */ onInputQueueCreated(InputQueue queue)40 void onInputQueueCreated(InputQueue queue); 41 42 /** 43 * Called when the given InputQueue is no longer associated with 44 * the thread and thus not dispatching events. 45 */ onInputQueueDestroyed(InputQueue queue)46 void onInputQueueDestroyed(InputQueue queue); 47 } 48 49 final InputChannel mChannel; 50 51 private static final Object sLock = new Object(); 52 nativeRegisterInputChannel(InputChannel inputChannel, InputHandler inputHandler, MessageQueue messageQueue)53 private static native void nativeRegisterInputChannel(InputChannel inputChannel, 54 InputHandler inputHandler, MessageQueue messageQueue); nativeUnregisterInputChannel(InputChannel inputChannel)55 private static native void nativeUnregisterInputChannel(InputChannel inputChannel); nativeFinished(long finishedToken, boolean handled)56 private static native void nativeFinished(long finishedToken, boolean handled); 57 58 /** @hide */ InputQueue(InputChannel channel)59 public InputQueue(InputChannel channel) { 60 mChannel = channel; 61 } 62 63 /** @hide */ getInputChannel()64 public InputChannel getInputChannel() { 65 return mChannel; 66 } 67 68 /** 69 * Registers an input channel and handler. 70 * @param inputChannel The input channel to register. 71 * @param inputHandler The input handler to input events send to the target. 72 * @param messageQueue The message queue on whose thread the handler should be invoked. 73 * @hide 74 */ registerInputChannel(InputChannel inputChannel, InputHandler inputHandler, MessageQueue messageQueue)75 public static void registerInputChannel(InputChannel inputChannel, InputHandler inputHandler, 76 MessageQueue messageQueue) { 77 if (inputChannel == null) { 78 throw new IllegalArgumentException("inputChannel must not be null"); 79 } 80 if (inputHandler == null) { 81 throw new IllegalArgumentException("inputHandler must not be null"); 82 } 83 if (messageQueue == null) { 84 throw new IllegalArgumentException("messageQueue must not be null"); 85 } 86 87 synchronized (sLock) { 88 if (DEBUG) { 89 Slog.d(TAG, "Registering input channel '" + inputChannel + "'"); 90 } 91 92 nativeRegisterInputChannel(inputChannel, inputHandler, messageQueue); 93 } 94 } 95 96 /** 97 * Unregisters an input channel. 98 * Does nothing if the channel is not currently registered. 99 * @param inputChannel The input channel to unregister. 100 * @hide 101 */ unregisterInputChannel(InputChannel inputChannel)102 public static void unregisterInputChannel(InputChannel inputChannel) { 103 if (inputChannel == null) { 104 throw new IllegalArgumentException("inputChannel must not be null"); 105 } 106 107 synchronized (sLock) { 108 if (DEBUG) { 109 Slog.d(TAG, "Unregistering input channel '" + inputChannel + "'"); 110 } 111 112 nativeUnregisterInputChannel(inputChannel); 113 } 114 } 115 116 @SuppressWarnings("unused") dispatchKeyEvent(InputHandler inputHandler, KeyEvent event, long finishedToken)117 private static void dispatchKeyEvent(InputHandler inputHandler, 118 KeyEvent event, long finishedToken) { 119 FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken); 120 inputHandler.handleKey(event, finishedCallback); 121 } 122 123 @SuppressWarnings("unused") dispatchMotionEvent(InputHandler inputHandler, MotionEvent event, long finishedToken)124 private static void dispatchMotionEvent(InputHandler inputHandler, 125 MotionEvent event, long finishedToken) { 126 FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken); 127 inputHandler.handleMotion(event, finishedCallback); 128 } 129 130 /** 131 * A callback that must be invoked to when finished processing an event. 132 * @hide 133 */ 134 public static final class FinishedCallback { 135 private static final boolean DEBUG_RECYCLING = false; 136 137 private static final int RECYCLE_MAX_COUNT = 4; 138 139 private static FinishedCallback sRecycleHead; 140 private static int sRecycleCount; 141 142 private FinishedCallback mRecycleNext; 143 private long mFinishedToken; 144 FinishedCallback()145 private FinishedCallback() { 146 } 147 obtain(long finishedToken)148 public static FinishedCallback obtain(long finishedToken) { 149 synchronized (sLock) { 150 FinishedCallback callback = sRecycleHead; 151 if (callback != null) { 152 sRecycleHead = callback.mRecycleNext; 153 sRecycleCount -= 1; 154 callback.mRecycleNext = null; 155 } else { 156 callback = new FinishedCallback(); 157 } 158 callback.mFinishedToken = finishedToken; 159 return callback; 160 } 161 } 162 finished(boolean handled)163 public void finished(boolean handled) { 164 synchronized (sLock) { 165 if (mFinishedToken == -1) { 166 throw new IllegalStateException("Event finished callback already invoked."); 167 } 168 169 nativeFinished(mFinishedToken, handled); 170 mFinishedToken = -1; 171 172 if (sRecycleCount < RECYCLE_MAX_COUNT) { 173 mRecycleNext = sRecycleHead; 174 sRecycleHead = this; 175 sRecycleCount += 1; 176 177 if (DEBUG_RECYCLING) { 178 Slog.d(TAG, "Recycled finished callbacks: " + sRecycleCount); 179 } 180 } 181 } 182 } 183 } 184 } 185