/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import android.annotation.UnsupportedAppUsage; import android.os.Looper; import android.os.MessageQueue; import android.util.Log; import dalvik.system.CloseGuard; import java.lang.ref.WeakReference; /** * Provides a low-level mechanism for an application to send input events. * @hide */ public abstract class InputEventSender { private static final String TAG = "InputEventSender"; private final CloseGuard mCloseGuard = CloseGuard.get(); private long mSenderPtr; // We keep references to the input channel and message queue objects here so that // they are not GC'd while the native peer of the receiver is using them. private InputChannel mInputChannel; private MessageQueue mMessageQueue; private static native long nativeInit(WeakReference sender, InputChannel inputChannel, MessageQueue messageQueue); private static native void nativeDispose(long senderPtr); private static native boolean nativeSendKeyEvent(long senderPtr, int seq, KeyEvent event); private static native boolean nativeSendMotionEvent(long senderPtr, int seq, MotionEvent event); /** * Creates an input event sender bound to the specified input channel. * * @param inputChannel The input channel. * @param looper The looper to use when invoking callbacks. */ public InputEventSender(InputChannel inputChannel, Looper looper) { if (inputChannel == null) { throw new IllegalArgumentException("inputChannel must not be null"); } if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mInputChannel = inputChannel; mMessageQueue = looper.getQueue(); mSenderPtr = nativeInit(new WeakReference(this), inputChannel, mMessageQueue); mCloseGuard.open("dispose"); } @Override protected void finalize() throws Throwable { try { dispose(true); } finally { super.finalize(); } } /** * Disposes the receiver. */ public void dispose() { dispose(false); } private void dispose(boolean finalized) { if (mCloseGuard != null) { if (finalized) { mCloseGuard.warnIfOpen(); } mCloseGuard.close(); } if (mSenderPtr != 0) { nativeDispose(mSenderPtr); mSenderPtr = 0; } mInputChannel = null; mMessageQueue = null; } /** * Called when an input event is finished. * * @param seq The input event sequence number. * @param handled True if the input event was handled. */ public void onInputEventFinished(int seq, boolean handled) { } /** * Sends an input event. * Must be called on the same Looper thread to which the sender is attached. * * @param seq The input event sequence number. * @param event The input event to send. * @return True if the entire event was sent successfully. May return false * if the input channel buffer filled before all samples were dispatched. */ public final boolean sendInputEvent(int seq, InputEvent event) { if (event == null) { throw new IllegalArgumentException("event must not be null"); } if (mSenderPtr == 0) { Log.w(TAG, "Attempted to send an input event but the input event " + "sender has already been disposed."); return false; } if (event instanceof KeyEvent) { return nativeSendKeyEvent(mSenderPtr, seq, (KeyEvent)event); } else { return nativeSendMotionEvent(mSenderPtr, seq, (MotionEvent)event); } } // Called from native code. @SuppressWarnings("unused") @UnsupportedAppUsage private void dispatchInputEventFinished(int seq, boolean handled) { onInputEventFinished(seq, handled); } }