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 android.annotation.UnsupportedAppUsage;
     20 import android.os.Looper;
     21 import android.os.MessageQueue;
     22 import android.util.Log;
     23 import android.util.SparseIntArray;
     24 
     25 import dalvik.system.CloseGuard;
     26 
     27 import java.lang.ref.WeakReference;
     28 
     29 /**
     30  * Provides a low-level mechanism for an application to receive input events.
     31  * @hide
     32  */
     33 public abstract class InputEventReceiver {
     34     private static final String TAG = "InputEventReceiver";
     35 
     36     private final CloseGuard mCloseGuard = CloseGuard.get();
     37 
     38     private long mReceiverPtr;
     39 
     40     // We keep references to the input channel and message queue objects here so that
     41     // they are not GC'd while the native peer of the receiver is using them.
     42     private InputChannel mInputChannel;
     43     private MessageQueue mMessageQueue;
     44 
     45     // Map from InputEvent sequence numbers to dispatcher sequence numbers.
     46     private final SparseIntArray mSeqMap = new SparseIntArray();
     47 
     48     private static native long nativeInit(WeakReference<InputEventReceiver> receiver,
     49             InputChannel inputChannel, MessageQueue messageQueue);
     50     private static native void nativeDispose(long receiverPtr);
     51     private static native void nativeFinishInputEvent(long receiverPtr, int seq, boolean handled);
     52     private static native boolean nativeConsumeBatchedInputEvents(long receiverPtr,
     53             long frameTimeNanos);
     54 
     55     /**
     56      * Creates an input event receiver bound to the specified input channel.
     57      *
     58      * @param inputChannel The input channel.
     59      * @param looper The looper to use when invoking callbacks.
     60      */
     61     public InputEventReceiver(InputChannel inputChannel, Looper looper) {
     62         if (inputChannel == null) {
     63             throw new IllegalArgumentException("inputChannel must not be null");
     64         }
     65         if (looper == null) {
     66             throw new IllegalArgumentException("looper must not be null");
     67         }
     68 
     69         mInputChannel = inputChannel;
     70         mMessageQueue = looper.getQueue();
     71         mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
     72                 inputChannel, mMessageQueue);
     73 
     74         mCloseGuard.open("dispose");
     75     }
     76 
     77     @Override
     78     protected void finalize() throws Throwable {
     79         try {
     80             dispose(true);
     81         } finally {
     82             super.finalize();
     83         }
     84     }
     85 
     86     /**
     87      * Disposes the receiver.
     88      */
     89     public void dispose() {
     90         dispose(false);
     91     }
     92 
     93     private void dispose(boolean finalized) {
     94         if (mCloseGuard != null) {
     95             if (finalized) {
     96                 mCloseGuard.warnIfOpen();
     97             }
     98             mCloseGuard.close();
     99         }
    100 
    101         if (mReceiverPtr != 0) {
    102             nativeDispose(mReceiverPtr);
    103             mReceiverPtr = 0;
    104         }
    105         mInputChannel = null;
    106         mMessageQueue = null;
    107     }
    108 
    109     /**
    110      * Called when an input event is received.
    111      * The recipient should process the input event and then call {@link #finishInputEvent}
    112      * to indicate whether the event was handled.  No new input events will be received
    113      * until {@link #finishInputEvent} is called.
    114      *
    115      * @param event The input event that was received.
    116      */
    117     @UnsupportedAppUsage
    118     public void onInputEvent(InputEvent event) {
    119         finishInputEvent(event, false);
    120     }
    121 
    122     /**
    123      * Called when a batched input event is pending.
    124      *
    125      * The batched input event will continue to accumulate additional movement
    126      * samples until the recipient calls {@link #consumeBatchedInputEvents} or
    127      * an event is received that ends the batch and causes it to be consumed
    128      * immediately (such as a pointer up event).
    129      */
    130     public void onBatchedInputEventPending() {
    131         consumeBatchedInputEvents(-1);
    132     }
    133 
    134     /**
    135      * Finishes an input event and indicates whether it was handled.
    136      * Must be called on the same Looper thread to which the receiver is attached.
    137      *
    138      * @param event The input event that was finished.
    139      * @param handled True if the event was handled.
    140      */
    141     public final void finishInputEvent(InputEvent event, boolean handled) {
    142         if (event == null) {
    143             throw new IllegalArgumentException("event must not be null");
    144         }
    145         if (mReceiverPtr == 0) {
    146             Log.w(TAG, "Attempted to finish an input event but the input event "
    147                     + "receiver has already been disposed.");
    148         } else {
    149             int index = mSeqMap.indexOfKey(event.getSequenceNumber());
    150             if (index < 0) {
    151                 Log.w(TAG, "Attempted to finish an input event that is not in progress.");
    152             } else {
    153                 int seq = mSeqMap.valueAt(index);
    154                 mSeqMap.removeAt(index);
    155                 nativeFinishInputEvent(mReceiverPtr, seq, handled);
    156             }
    157         }
    158         event.recycleIfNeededAfterDispatch();
    159     }
    160 
    161     /**
    162      * Consumes all pending batched input events.
    163      * Must be called on the same Looper thread to which the receiver is attached.
    164      *
    165      * This method forces all batched input events to be delivered immediately.
    166      * Should be called just before animating or drawing a new frame in the UI.
    167      *
    168      * @param frameTimeNanos The time in the {@link System#nanoTime()} time base
    169      * when the current display frame started rendering, or -1 if unknown.
    170      *
    171      * @return Whether a batch was consumed
    172      */
    173     public final boolean consumeBatchedInputEvents(long frameTimeNanos) {
    174         if (mReceiverPtr == 0) {
    175             Log.w(TAG, "Attempted to consume batched input events but the input event "
    176                     + "receiver has already been disposed.");
    177         } else {
    178             return nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos);
    179         }
    180         return false;
    181     }
    182 
    183     // Called from native code.
    184     @SuppressWarnings("unused")
    185     @UnsupportedAppUsage
    186     private void dispatchInputEvent(int seq, InputEvent event) {
    187         mSeqMap.put(event.getSequenceNumber(), seq);
    188         onInputEvent(event);
    189     }
    190 
    191     // Called from native code.
    192     @SuppressWarnings("unused")
    193     @UnsupportedAppUsage
    194     private void dispatchBatchedInputEventPending() {
    195         onBatchedInputEventPending();
    196     }
    197 
    198     public static interface Factory {
    199         public InputEventReceiver createInputEventReceiver(
    200                 InputChannel inputChannel, Looper looper);
    201     }
    202 }
    203