• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)56     private static native void nativeFinished(long finishedToken);
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         Runnable 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         Runnable finishedCallback = FinishedCallback.obtain(finishedToken);
127         inputHandler.handleMotion(event, finishedCallback);
128     }
129 
130     private static class FinishedCallback implements Runnable {
131         private static final boolean DEBUG_RECYCLING = false;
132 
133         private static final int RECYCLE_MAX_COUNT = 4;
134 
135         private static FinishedCallback sRecycleHead;
136         private static int sRecycleCount;
137 
138         private FinishedCallback mRecycleNext;
139         private long mFinishedToken;
140 
FinishedCallback()141         private FinishedCallback() {
142         }
143 
obtain(long finishedToken)144         public static FinishedCallback obtain(long finishedToken) {
145             synchronized (sLock) {
146                 FinishedCallback callback = sRecycleHead;
147                 if (callback != null) {
148                     sRecycleHead = callback.mRecycleNext;
149                     sRecycleCount -= 1;
150                     callback.mRecycleNext = null;
151                 } else {
152                     callback = new FinishedCallback();
153                 }
154                 callback.mFinishedToken = finishedToken;
155                 return callback;
156             }
157         }
158 
run()159         public void run() {
160             synchronized (sLock) {
161                 if (mFinishedToken == -1) {
162                     throw new IllegalStateException("Event finished callback already invoked.");
163                 }
164 
165                 nativeFinished(mFinishedToken);
166                 mFinishedToken = -1;
167 
168                 if (sRecycleCount < RECYCLE_MAX_COUNT) {
169                     mRecycleNext = sRecycleHead;
170                     sRecycleHead = this;
171                     sRecycleCount += 1;
172 
173                     if (DEBUG_RECYCLING) {
174                         Slog.d(TAG, "Recycled finished callbacks: " + sRecycleCount);
175                     }
176                 }
177             }
178         }
179     }
180 }
181