| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chromoting; |
| 6 |
| 7 import android.graphics.Point; |
| 8 import android.view.KeyEvent; |
| 9 import android.view.MotionEvent; |
| 10 |
| 11 import org.chromium.chromoting.jni.TouchEventData; |
| 12 |
| 13 import java.util.ArrayList; |
| 14 import java.util.List; |
| 15 import java.util.Set; |
| 16 import java.util.TreeSet; |
| 17 |
| 18 /** |
| 19 * A set of functions to send users' activities, which are represented by Androi
d classes, to |
| 20 * remote host machine. This class uses a {@link InputStub} to do the real injec
tions. |
| 21 */ |
| 22 public final class InputEventSender { |
| 23 private static final int[] CTRL_ALT_DEL = { |
| 24 KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYC
ODE_FORWARD_DEL, |
| 25 }; |
| 26 |
| 27 private final InputStub mInjector; |
| 28 |
| 29 /** Set of pressed keys for which we've sent TextEvent. */ |
| 30 private final Set<Integer> mPressedTextKeys; |
| 31 |
| 32 public InputEventSender(InputStub injector) { |
| 33 Preconditions.notNull(injector); |
| 34 mInjector = injector; |
| 35 mPressedTextKeys = new TreeSet<>(); |
| 36 } |
| 37 |
| 38 public void sendMouseEvent(Point pos, int button, boolean down) { |
| 39 Preconditions.isTrue(button == InputStub.BUTTON_UNDEFINED |
| 40 || button == InputStub.BUTTON_LEFT |
| 41 || button == InputStub.BUTTON_MIDDLE |
| 42 || button == InputStub.BUTTON_RIGHT); |
| 43 mInjector.sendMouseEvent(pos.x, pos.y, button, down); |
| 44 } |
| 45 |
| 46 public void sendMouseDown(Point pos, int button) { |
| 47 sendMouseEvent(pos, button, true); |
| 48 } |
| 49 |
| 50 public void sendMouseUp(Point pos, int button) { |
| 51 sendMouseEvent(pos, button, false); |
| 52 } |
| 53 |
| 54 public void sendMouseClick(Point pos, int button) { |
| 55 sendMouseDown(pos, button); |
| 56 sendMouseUp(pos, button); |
| 57 } |
| 58 |
| 59 public void sendCursorMove(Point pos) { |
| 60 sendMouseUp(pos, InputStub.BUTTON_UNDEFINED); |
| 61 } |
| 62 |
| 63 // TODO(zijiehe): This function will be eventually removed after {@link Inpu
tStrategyInterface} |
| 64 // has been deprecated. |
| 65 public void sendCursorMove(int x, int y) { |
| 66 sendCursorMove(new Point(x, y)); |
| 67 } |
| 68 |
| 69 public void sendMouseWheelEvent(float distanceX, float distanceY) { |
| 70 mInjector.sendMouseWheelEvent((int) distanceX, (int) distanceY); |
| 71 } |
| 72 |
| 73 public void sendReverseMouseWheelEvent(float distanceX, float distanceY) { |
| 74 sendMouseWheelEvent(-distanceX, -distanceY); |
| 75 } |
| 76 |
| 77 /** |
| 78 * Extracts the touch point data from a MotionEvent, converts each point int
o a marshallable |
| 79 * object and passes the set of points to the JNI layer to be transmitted to
the remote host. |
| 80 * |
| 81 * @param event The event to send to the remote host for injection. NOTE: T
his object must be |
| 82 * updated to represent the remote machine's coordinate system
before calling this |
| 83 * function. |
| 84 */ |
| 85 public void sendTouchEvent(MotionEvent event) { |
| 86 int action = event.getActionMasked(); |
| 87 TouchEventData.EventType touchEventType = TouchEventData.EventType.fromM
askedAction(action); |
| 88 List<TouchEventData> touchEventList = new ArrayList<TouchEventData>(); |
| 89 |
| 90 if (action == MotionEvent.ACTION_MOVE) { |
| 91 // In order to process all of the events associated with an ACTION_M
OVE event, we need |
| 92 // to walk the list of historical events in order and add each event
to our list, then |
| 93 // retrieve the current move event data. |
| 94 int pointerCount = event.getPointerCount(); |
| 95 int historySize = event.getHistorySize(); |
| 96 for (int h = 0; h < historySize; ++h) { |
| 97 for (int p = 0; p < pointerCount; ++p) { |
| 98 touchEventList.add(new TouchEventData(event.getPointerId(p), |
| 99 event.getHistoricalX(p, h), event.getHistoricalY(p,
h), |
| 100 event.getHistoricalSize(p, h), event.getHistoricalSi
ze(p, h), |
| 101 event.getHistoricalOrientation(p, h), |
| 102 event.getHistoricalPressure(p, h))); |
| 103 } |
| 104 } |
| 105 |
| 106 for (int p = 0; p < pointerCount; p++) { |
| 107 touchEventList.add(new TouchEventData(event.getPointerId(p), eve
nt.getX(p), |
| 108 event.getY(p), event.getSize(p), event.getSize(p), event
.getOrientation(p), |
| 109 event.getPressure(p))); |
| 110 } |
| 111 } else { |
| 112 // For all other events, we only want to grab the current/active poi
nter. The event |
| 113 // contains a list of every active pointer but passing all of of the
se to the host can |
| 114 // cause confusion on the remote OS side and result in broken touch
gestures. |
| 115 int activePointerIndex = event.getActionIndex(); |
| 116 touchEventList.add(new TouchEventData(event.getPointerId(activePoint
erIndex), |
| 117 event.getX(activePointerIndex), event.getY(activePointerInde
x), |
| 118 event.getSize(activePointerIndex), event.getSize(activePoint
erIndex), |
| 119 event.getOrientation(activePointerIndex), |
| 120 event.getPressure(activePointerIndex))); |
| 121 } |
| 122 |
| 123 if (!touchEventList.isEmpty()) { |
| 124 mInjector.sendTouchEvent(touchEventType, touchEventList.toArray(new
TouchEventData[0])); |
| 125 } |
| 126 } |
| 127 |
| 128 /** |
| 129 * Converts the {@link KeyEvent} into low-level events and sends them to the
host as either |
| 130 * key-events or text-events. This contains some logic for handling some spe
cial keys, and |
| 131 * avoids sending a key-up event for a key that was previously injected as a
text-event. |
| 132 */ |
| 133 public boolean sendKeyEvent(KeyEvent event) { |
| 134 int keyCode = event.getKeyCode(); |
| 135 boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; |
| 136 |
| 137 // Events received from software keyboards generate TextEvent in two |
| 138 // cases: |
| 139 // 1. This is an ACTION_MULTIPLE event. |
| 140 // 2. Ctrl, Alt and Meta are not pressed. |
| 141 // This ensures that on-screen keyboard always injects input that |
| 142 // correspond to what user sees on the screen, while physical keyboard |
| 143 // acts as if it is connected to the remote host. |
| 144 if (event.getAction() == KeyEvent.ACTION_MULTIPLE) { |
| 145 mInjector.sendTextEvent(event.getCharacters()); |
| 146 return true; |
| 147 } |
| 148 |
| 149 // For Enter getUnicodeChar() returns 10 (line feed), but we still |
| 150 // want to send it as KeyEvent. |
| 151 int unicode = keyCode != KeyEvent.KEYCODE_ENTER ? event.getUnicodeChar()
: 0; |
| 152 |
| 153 boolean no_modifiers = |
| 154 !event.isAltPressed() && !event.isCtrlPressed() && !event.isMeta
Pressed(); |
| 155 |
| 156 if (pressed && unicode != 0 && no_modifiers) { |
| 157 mPressedTextKeys.add(keyCode); |
| 158 int[] codePoints = {unicode}; |
| 159 mInjector.sendTextEvent(new String(codePoints, 0, 1)); |
| 160 return true; |
| 161 } |
| 162 |
| 163 if (!pressed && mPressedTextKeys.contains(keyCode)) { |
| 164 mPressedTextKeys.remove(keyCode); |
| 165 return true; |
| 166 } |
| 167 |
| 168 switch (keyCode) { |
| 169 // KEYCODE_AT, KEYCODE_POUND, KEYCODE_STAR and KEYCODE_PLUS are |
| 170 // deprecated, but they still need to be here for older devices and |
| 171 // third-party keyboards that may still generate these events. See |
| 172 // https://source.android.com/devices/input/keyboard-devices.html#le
gacy-unsupported-keys |
| 173 case KeyEvent.KEYCODE_AT: |
| 174 mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); |
| 175 mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed); |
| 176 return true; |
| 177 |
| 178 case KeyEvent.KEYCODE_POUND: |
| 179 mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); |
| 180 mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed); |
| 181 return true; |
| 182 |
| 183 case KeyEvent.KEYCODE_STAR: |
| 184 mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); |
| 185 mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed); |
| 186 return true; |
| 187 |
| 188 case KeyEvent.KEYCODE_PLUS: |
| 189 mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); |
| 190 mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed); |
| 191 return true; |
| 192 |
| 193 default: |
| 194 // We try to send all other key codes to the host directly. |
| 195 return mInjector.sendKeyEvent(0, keyCode, pressed); |
| 196 } |
| 197 } |
| 198 |
| 199 public void sendKeyDown(int keyCode) { |
| 200 mInjector.sendKeyEvent(0, keyCode, true); |
| 201 } |
| 202 |
| 203 public void sendKeyUp(int keyCode) { |
| 204 mInjector.sendKeyEvent(0, keyCode, false); |
| 205 } |
| 206 |
| 207 /** |
| 208 * Sends key combinations such as Ctrl-Alt-Del. This injects a key-down even
t for every key in |
| 209 * the list, and then injects the corresponding key-up events. If null or an
empty |keyCodes| |
| 210 * is passed, nothing will be injected. |
| 211 */ |
| 212 public void sendKeysPress(int[] keyCodes) { |
| 213 if (keyCodes != null && keyCodes.length > 0) { |
| 214 for (int keyCode : keyCodes) { |
| 215 sendKeyDown(keyCode); |
| 216 } |
| 217 for (int keyCode : keyCodes) { |
| 218 sendKeyUp(keyCode); |
| 219 } |
| 220 } |
| 221 } |
| 222 |
| 223 public void sendCtrlAltDel() { |
| 224 sendKeysPress(CTRL_ALT_DEL); |
| 225 } |
| 226 } |
| OLD | NEW |