| Index: remoting/android/java/src/org/chromium/chromoting/InputEventSender.java
|
| diff --git a/remoting/android/java/src/org/chromium/chromoting/InputEventSender.java b/remoting/android/java/src/org/chromium/chromoting/InputEventSender.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..514c2af1bd26b0d935dc595629f28ca87af16834
|
| --- /dev/null
|
| +++ b/remoting/android/java/src/org/chromium/chromoting/InputEventSender.java
|
| @@ -0,0 +1,226 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.chromoting;
|
| +
|
| +import android.graphics.Point;
|
| +import android.view.KeyEvent;
|
| +import android.view.MotionEvent;
|
| +
|
| +import org.chromium.chromoting.jni.TouchEventData;
|
| +
|
| +import java.util.ArrayList;
|
| +import java.util.List;
|
| +import java.util.Set;
|
| +import java.util.TreeSet;
|
| +
|
| +/**
|
| + * A set of functions to send users' activities, which are represented by Android classes, to
|
| + * remote host machine. This class uses a {@link InputStub} to do the real injections.
|
| + */
|
| +public final class InputEventSender {
|
| + private static final int[] CTRL_ALT_DEL = {
|
| + KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_FORWARD_DEL,
|
| + };
|
| +
|
| + private final InputStub mInjector;
|
| +
|
| + /** Set of pressed keys for which we've sent TextEvent. */
|
| + private final Set<Integer> mPressedTextKeys;
|
| +
|
| + public InputEventSender(InputStub injector) {
|
| + Preconditions.notNull(injector);
|
| + mInjector = injector;
|
| + mPressedTextKeys = new TreeSet<>();
|
| + }
|
| +
|
| + public void sendMouseEvent(Point pos, int button, boolean down) {
|
| + Preconditions.isTrue(button == InputStub.BUTTON_UNDEFINED
|
| + || button == InputStub.BUTTON_LEFT
|
| + || button == InputStub.BUTTON_MIDDLE
|
| + || button == InputStub.BUTTON_RIGHT);
|
| + mInjector.sendMouseEvent(pos.x, pos.y, button, down);
|
| + }
|
| +
|
| + public void sendMouseDown(Point pos, int button) {
|
| + sendMouseEvent(pos, button, true);
|
| + }
|
| +
|
| + public void sendMouseUp(Point pos, int button) {
|
| + sendMouseEvent(pos, button, false);
|
| + }
|
| +
|
| + public void sendMouseClick(Point pos, int button) {
|
| + sendMouseDown(pos, button);
|
| + sendMouseUp(pos, button);
|
| + }
|
| +
|
| + public void sendCursorMove(Point pos) {
|
| + sendMouseUp(pos, InputStub.BUTTON_UNDEFINED);
|
| + }
|
| +
|
| + // TODO(zijiehe): This function will be eventually removed after {@link InputStrategyInterface}
|
| + // has been deprecated.
|
| + public void sendCursorMove(int x, int y) {
|
| + sendCursorMove(new Point(x, y));
|
| + }
|
| +
|
| + public void sendMouseWheelEvent(float distanceX, float distanceY) {
|
| + mInjector.sendMouseWheelEvent((int) distanceX, (int) distanceY);
|
| + }
|
| +
|
| + public void sendReverseMouseWheelEvent(float distanceX, float distanceY) {
|
| + sendMouseWheelEvent(-distanceX, -distanceY);
|
| + }
|
| +
|
| + /**
|
| + * Extracts the touch point data from a MotionEvent, converts each point into a marshallable
|
| + * object and passes the set of points to the JNI layer to be transmitted to the remote host.
|
| + *
|
| + * @param event The event to send to the remote host for injection. NOTE: This object must be
|
| + * updated to represent the remote machine's coordinate system before calling this
|
| + * function.
|
| + */
|
| + public void sendTouchEvent(MotionEvent event) {
|
| + int action = event.getActionMasked();
|
| + TouchEventData.EventType touchEventType = TouchEventData.EventType.fromMaskedAction(action);
|
| + List<TouchEventData> touchEventList = new ArrayList<TouchEventData>();
|
| +
|
| + if (action == MotionEvent.ACTION_MOVE) {
|
| + // In order to process all of the events associated with an ACTION_MOVE event, we need
|
| + // to walk the list of historical events in order and add each event to our list, then
|
| + // retrieve the current move event data.
|
| + int pointerCount = event.getPointerCount();
|
| + int historySize = event.getHistorySize();
|
| + for (int h = 0; h < historySize; ++h) {
|
| + for (int p = 0; p < pointerCount; ++p) {
|
| + touchEventList.add(new TouchEventData(event.getPointerId(p),
|
| + event.getHistoricalX(p, h), event.getHistoricalY(p, h),
|
| + event.getHistoricalSize(p, h), event.getHistoricalSize(p, h),
|
| + event.getHistoricalOrientation(p, h),
|
| + event.getHistoricalPressure(p, h)));
|
| + }
|
| + }
|
| +
|
| + for (int p = 0; p < pointerCount; p++) {
|
| + touchEventList.add(new TouchEventData(event.getPointerId(p), event.getX(p),
|
| + event.getY(p), event.getSize(p), event.getSize(p), event.getOrientation(p),
|
| + event.getPressure(p)));
|
| + }
|
| + } else {
|
| + // For all other events, we only want to grab the current/active pointer. The event
|
| + // contains a list of every active pointer but passing all of of these to the host can
|
| + // cause confusion on the remote OS side and result in broken touch gestures.
|
| + int activePointerIndex = event.getActionIndex();
|
| + touchEventList.add(new TouchEventData(event.getPointerId(activePointerIndex),
|
| + event.getX(activePointerIndex), event.getY(activePointerIndex),
|
| + event.getSize(activePointerIndex), event.getSize(activePointerIndex),
|
| + event.getOrientation(activePointerIndex),
|
| + event.getPressure(activePointerIndex)));
|
| + }
|
| +
|
| + if (!touchEventList.isEmpty()) {
|
| + mInjector.sendTouchEvent(touchEventType, touchEventList.toArray(new TouchEventData[0]));
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Converts the {@link KeyEvent} into low-level events and sends them to the host as either
|
| + * key-events or text-events. This contains some logic for handling some special keys, and
|
| + * avoids sending a key-up event for a key that was previously injected as a text-event.
|
| + */
|
| + public boolean sendKeyEvent(KeyEvent event) {
|
| + int keyCode = event.getKeyCode();
|
| + boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN;
|
| +
|
| + // Events received from software keyboards generate TextEvent in two
|
| + // cases:
|
| + // 1. This is an ACTION_MULTIPLE event.
|
| + // 2. Ctrl, Alt and Meta are not pressed.
|
| + // This ensures that on-screen keyboard always injects input that
|
| + // correspond to what user sees on the screen, while physical keyboard
|
| + // acts as if it is connected to the remote host.
|
| + if (event.getAction() == KeyEvent.ACTION_MULTIPLE) {
|
| + mInjector.sendTextEvent(event.getCharacters());
|
| + return true;
|
| + }
|
| +
|
| + // For Enter getUnicodeChar() returns 10 (line feed), but we still
|
| + // want to send it as KeyEvent.
|
| + int unicode = keyCode != KeyEvent.KEYCODE_ENTER ? event.getUnicodeChar() : 0;
|
| +
|
| + boolean no_modifiers =
|
| + !event.isAltPressed() && !event.isCtrlPressed() && !event.isMetaPressed();
|
| +
|
| + if (pressed && unicode != 0 && no_modifiers) {
|
| + mPressedTextKeys.add(keyCode);
|
| + int[] codePoints = {unicode};
|
| + mInjector.sendTextEvent(new String(codePoints, 0, 1));
|
| + return true;
|
| + }
|
| +
|
| + if (!pressed && mPressedTextKeys.contains(keyCode)) {
|
| + mPressedTextKeys.remove(keyCode);
|
| + return true;
|
| + }
|
| +
|
| + switch (keyCode) {
|
| + // KEYCODE_AT, KEYCODE_POUND, KEYCODE_STAR and KEYCODE_PLUS are
|
| + // deprecated, but they still need to be here for older devices and
|
| + // third-party keyboards that may still generate these events. See
|
| + // https://source.android.com/devices/input/keyboard-devices.html#legacy-unsupported-keys
|
| + case KeyEvent.KEYCODE_AT:
|
| + mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
|
| + mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed);
|
| + return true;
|
| +
|
| + case KeyEvent.KEYCODE_POUND:
|
| + mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
|
| + mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed);
|
| + return true;
|
| +
|
| + case KeyEvent.KEYCODE_STAR:
|
| + mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
|
| + mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed);
|
| + return true;
|
| +
|
| + case KeyEvent.KEYCODE_PLUS:
|
| + mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
|
| + mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed);
|
| + return true;
|
| +
|
| + default:
|
| + // We try to send all other key codes to the host directly.
|
| + return mInjector.sendKeyEvent(0, keyCode, pressed);
|
| + }
|
| + }
|
| +
|
| + public void sendKeyDown(int keyCode) {
|
| + mInjector.sendKeyEvent(0, keyCode, true);
|
| + }
|
| +
|
| + public void sendKeyUp(int keyCode) {
|
| + mInjector.sendKeyEvent(0, keyCode, false);
|
| + }
|
| +
|
| + /**
|
| + * Sends key combinations such as Ctrl-Alt-Del. This injects a key-down event for every key in
|
| + * the list, and then injects the corresponding key-up events. If null or an empty |keyCodes|
|
| + * is passed, nothing will be injected.
|
| + */
|
| + public void sendKeysPress(int[] keyCodes) {
|
| + if (keyCodes != null && keyCodes.length > 0) {
|
| + for (int keyCode : keyCodes) {
|
| + sendKeyDown(keyCode);
|
| + }
|
| + for (int keyCode : keyCodes) {
|
| + sendKeyUp(keyCode);
|
| + }
|
| + }
|
| + }
|
| +
|
| + public void sendCtrlAltDel() {
|
| + sendKeysPress(CTRL_ALT_DEL);
|
| + }
|
| +}
|
|
|