Home | History | Annotate | Download | only in view
      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          */
     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          */
     46         void onInputQueueDestroyed(InputQueue queue);
     47     }
     48 
     49     final InputChannel mChannel;
     50 
     51     private static final Object sLock = new Object();
     52 
     53     private static native void nativeRegisterInputChannel(InputChannel inputChannel,
     54             InputHandler inputHandler, MessageQueue messageQueue);
     55     private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
     56     private static native void nativeFinished(long finishedToken, boolean handled);
     57 
     58     /** @hide */
     59     public InputQueue(InputChannel channel) {
     60         mChannel = channel;
     61     }
     62 
     63     /** @hide */
     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      */
     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      */
    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")
    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")
    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 
    145         private FinishedCallback() {
    146         }
    147 
    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 
    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