Home | History | Annotate | Download | only in view
      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 
     47     private static native long nativeInit(WeakReference<InputEventReceiver> receiver,
     48             InputChannel inputChannel, MessageQueue messageQueue);
     49     private static native void nativeDispose(long receiverPtr);
     50     private static native void nativeFinishInputEvent(long receiverPtr, int seq, boolean handled);
     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      */
     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
     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      */
     88     public void dispose() {
     89         dispose(false);
     90     }
     91 
     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      */
    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      */
    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      */
    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      */
    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")
    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")
    190     private void dispatchBatchedInputEventPending() {
    191         onBatchedInputEventPending();
    192     }
    193 
    194     public static interface Factory {
    195         public InputEventReceiver createInputEventReceiver(
    196                 InputChannel inputChannel, Looper looper);
    197     }
    198 }
    199