Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Side by Side Diff: remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java

Issue 2066683003: [Chromoting] Add InputInjector and InputInjectorWrapper for easy unittesting (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Resolve review comments Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chromoting; 5 package org.chromium.chromoting;
6 6
7 import android.graphics.Matrix; 7 import android.graphics.Matrix;
8 import android.graphics.Point;
8 import android.view.MotionEvent; 9 import android.view.MotionEvent;
9 10
10 import org.chromium.base.VisibleForTesting;
11 import org.chromium.chromoting.jni.Client;
12 import org.chromium.chromoting.jni.TouchEventData;
13
14 import java.util.ArrayList;
15 import java.util.LinkedList; 11 import java.util.LinkedList;
16 import java.util.List;
17 import java.util.Queue; 12 import java.util.Queue;
18 13
19 /** 14 /**
20 * This class receives local touch input events and forwards them to the remote host. 15 * This class receives local touch input events and forwards them to the remote host.
21 * A queue of MotionEvents is built up and then either transmitted to the remote host if one of its 16 * A queue of MotionEvents is built up and then either transmitted to the remote host if one of its
22 * remote gesture handler methods is called (such as onScroll) or it is cleared if the current 17 * remote gesture handler methods is called (such as onScroll) or it is cleared if the current
23 * stream of events does not represent a remote gesture. 18 * stream of events does not represent a remote gesture.
24 * NOTE: Not all touch gestures are remoted. Touch input and gestures outside t he supported ones 19 * NOTE: Not all touch gestures are remoted. Touch input and gestures outside t he supported ones
25 * (which includes tapping and 2 finger panning) will either affect the lo cal canvas or 20 * (which includes tapping and 2 finger panning) will either affect the lo cal canvas or
26 * will be dropped/ignored. 21 * will be dropped/ignored.
27 */ 22 */
28 public class TouchInputStrategy implements InputStrategyInterface { 23 public class TouchInputStrategy implements InputStrategyInterface {
29 /** 24 /**
30 * This interface abstracts the injection mechanism to allow for unit testin g.
31 */
32 public interface RemoteInputInjector {
33 public void injectMouseEvent(int x, int y, int button, boolean buttonDow n);
34 public void injectTouchEvent(TouchEventData.EventType eventType, TouchEv entData[] data);
35 }
36
37 /**
38 * This class provides the default implementation for injecting remote event s.
39 */
40 private class DefaultInputInjector implements RemoteInputInjector {
41 @Override
42 public void injectMouseEvent(int x, int y, int button, boolean buttonDow n) {
43 mClient.sendMouseEvent(x, y, button, buttonDown);
44 }
45
46 @Override
47 public void injectTouchEvent(TouchEventData.EventType eventType, TouchEv entData[] data) {
48 mClient.sendTouchEvent(eventType, data);
49 }
50 }
51
52 /**
53 * Contains the maximum number of MotionEvents to store before cancelling th e current gesture. 25 * Contains the maximum number of MotionEvents to store before cancelling th e current gesture.
54 * The size is ~3x the largest number of events seen during any remotable ge sture sequence. 26 * The size is ~3x the largest number of events seen during any remotable ge sture sequence.
55 */ 27 */
56 private static final int QUEUED_EVENT_THRESHOLD = 50; 28 private static final int QUEUED_EVENT_THRESHOLD = 50;
57 29
58 /** 30 /**
59 * Contains the set of MotionEvents received for the current gesture candida te. If one of the 31 * Contains the set of MotionEvents received for the current gesture candida te. If one of the
60 * gesture handling methods is called, these queued events will be transmitt ed to the remote 32 * gesture handling methods is called, these queued events will be transmitt ed to the remote
61 * host for injection. The queue has a maximum size determined by |QUEUED_E VENT_THRESHOLD| to 33 * host for injection. The queue has a maximum size determined by |QUEUED_E VENT_THRESHOLD| to
62 * prevent a live memory leak where the queue grows unbounded during a local gesture (such as 34 * prevent a live memory leak where the queue grows unbounded during a local gesture (such as
63 * someone panning the local canvas continuously for several seconds/minutes ). 35 * someone panning the local canvas continuously for several seconds/minutes ).
64 */ 36 */
65 private Queue<MotionEvent> mQueuedEvents; 37 private Queue<MotionEvent> mQueuedEvents;
66 38
67 /** 39 /**
68 * Indicates that the events received should be treated as part of an active remote gesture. 40 * Indicates that the events received should be treated as part of an active remote gesture.
69 */ 41 */
70 private boolean mInRemoteGesture = false; 42 private boolean mInRemoteGesture = false;
71 43
72 /** 44 /**
73 * Indicates whether MotionEvents and gestures should be acted upon or ignor ed. This flag is 45 * Indicates whether MotionEvents and gestures should be acted upon or ignor ed. This flag is
74 * set when we believe that the current sequence of events is not something we should remote. 46 * set when we believe that the current sequence of events is not something we should remote.
75 */ 47 */
76 private boolean mIgnoreTouchEvents = false; 48 private boolean mIgnoreTouchEvents = false;
77 49
78 private final RenderData mRenderData; 50 private final RenderData mRenderData;
51 private final InputEventSender mInjector;
79 52
80 private final Client mClient; 53 public TouchInputStrategy(RenderData renderData, InputEventSender injector) {
81 54 Preconditions.notNull(injector);
82 private RemoteInputInjector mRemoteInputInjector;
83
84 public TouchInputStrategy(RenderData renderData, Client client) {
85 mRenderData = renderData; 55 mRenderData = renderData;
86 mClient = client; 56 mInjector = injector;
87 mRemoteInputInjector = new DefaultInputInjector();
88 mQueuedEvents = new LinkedList<MotionEvent>(); 57 mQueuedEvents = new LinkedList<MotionEvent>();
89 58
90 synchronized (mRenderData) { 59 synchronized (mRenderData) {
91 mRenderData.drawCursor = false; 60 mRenderData.drawCursor = false;
92 } 61 }
93 } 62 }
94 63
95 @Override 64 @Override
96 public boolean onTap(int button) { 65 public boolean onTap(int button) {
97 if (mQueuedEvents.isEmpty() || mIgnoreTouchEvents) { 66 if (mQueuedEvents.isEmpty() || mIgnoreTouchEvents) {
98 return false; 67 return false;
99 } 68 }
100 69
101 switch (button) { 70 switch (button) {
102 case TouchInputHandlerInterface.BUTTON_LEFT: 71 case InputStub.BUTTON_LEFT:
103 injectQueuedEvents(); 72 injectQueuedEvents();
104 return true; 73 return true;
105 74
106 case TouchInputHandlerInterface.BUTTON_RIGHT: 75 case InputStub.BUTTON_RIGHT:
107 // Using the mouse for right-clicking is consistent across all h ost platforms. 76 // Using the mouse for right-clicking is consistent across all h ost platforms.
108 // Right-click gestures are often platform specific and can be t ricky to simulate. 77 // Right-click gestures are often platform specific and can be t ricky to simulate.
109 78
110 // Grab the first queued event which should be the initial ACTIO N_DOWN event. 79 // Grab the first queued event which should be the initial ACTIO N_DOWN event.
111 MotionEvent downEvent = mQueuedEvents.peek(); 80 MotionEvent downEvent = mQueuedEvents.peek();
112 assert downEvent.getActionMasked() == MotionEvent.ACTION_DOWN; 81 assert downEvent.getActionMasked() == MotionEvent.ACTION_DOWN;
113 82
114 int x = (int) downEvent.getX(); 83 mInjector.sendMouseClick(new Point((int) downEvent.getX(), (int) downEvent.getY()),
115 int y = (int) downEvent.getY(); 84 InputStub.BUTTON_RIGHT);
116 mRemoteInputInjector.injectMouseEvent(
117 x, y, TouchInputHandlerInterface.BUTTON_RIGHT, true);
118 mRemoteInputInjector.injectMouseEvent(
119 x, y, TouchInputHandlerInterface.BUTTON_RIGHT, false);
120 clearQueuedEvents(); 85 clearQueuedEvents();
121 return true; 86 return true;
122 87
123 default: 88 default:
124 // Tap gestures for > 2 fingers are not supported. 89 // Tap gestures for > 2 fingers are not supported.
125 return false; 90 return false;
126 } 91 }
127 } 92 }
128 93
129 @Override 94 @Override
130 public boolean onPressAndHold(int button) { 95 public boolean onPressAndHold(int button) {
131 if (button != TouchInputHandlerInterface.BUTTON_LEFT || mQueuedEvents.is Empty() 96 if (button != InputStub.BUTTON_LEFT || mQueuedEvents.isEmpty()
132 || mIgnoreTouchEvents) { 97 || mIgnoreTouchEvents) {
133 return false; 98 return false;
134 } 99 }
135 100
136 mInRemoteGesture = true; 101 mInRemoteGesture = true;
137 injectQueuedEvents(); 102 injectQueuedEvents();
138 return true; 103 return true;
139 } 104 }
140 105
141 @Override 106 @Override
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
186 mQueuedEvents.add(transformToRemoteCoordinates(event)); 151 mQueuedEvents.add(transformToRemoteCoordinates(event));
187 } 152 }
188 break; 153 break;
189 154
190 case MotionEvent.ACTION_CANCEL: 155 case MotionEvent.ACTION_CANCEL:
191 case MotionEvent.ACTION_MOVE: 156 case MotionEvent.ACTION_MOVE:
192 case MotionEvent.ACTION_POINTER_UP: 157 case MotionEvent.ACTION_POINTER_UP:
193 case MotionEvent.ACTION_UP: 158 case MotionEvent.ACTION_UP:
194 event = transformToRemoteCoordinates(event); 159 event = transformToRemoteCoordinates(event);
195 if (mInRemoteGesture) { 160 if (mInRemoteGesture) {
196 injectTouchEventData(event); 161 mInjector.sendTouchEvent(event);
197 event.recycle(); 162 event.recycle();
198 } else { 163 } else {
199 mQueuedEvents.add(event); 164 mQueuedEvents.add(event);
200 } 165 }
201 break; 166 break;
202 167
203 default: 168 default:
204 break; 169 break;
205 } 170 }
206 } 171 }
207 172
208 @Override 173 @Override
209 public void injectCursorMoveEvent(int x, int y) {} 174 public void injectCursorMoveEvent(int x, int y) {}
210 175
211 @Override 176 @Override
212 public DesktopView.InputFeedbackType getShortPressFeedbackType() { 177 public DesktopView.InputFeedbackType getShortPressFeedbackType() {
213 return DesktopView.InputFeedbackType.NONE; 178 return DesktopView.InputFeedbackType.NONE;
214 } 179 }
215 180
216 @Override 181 @Override
217 public DesktopView.InputFeedbackType getLongPressFeedbackType() { 182 public DesktopView.InputFeedbackType getLongPressFeedbackType() {
218 return DesktopView.InputFeedbackType.LARGE_ANIMATION; 183 return DesktopView.InputFeedbackType.LARGE_ANIMATION;
219 } 184 }
220 185
221 @Override 186 @Override
222 public boolean isIndirectInputMode() { 187 public boolean isIndirectInputMode() {
223 return false; 188 return false;
224 } 189 }
225 190
226 @VisibleForTesting
227 protected void setRemoteInputInjectorForTest(RemoteInputInjector injector) {
228 mRemoteInputInjector = injector;
229 }
230
231 /**
232 * Extracts the touch point data from a MotionEvent, converts each point int o a marshallable
233 * object and passes the set of points to the JNI layer to be transmitted to the remote host.
234 *
235 * @param event The event to send to the remote host for injection. NOTE: T his object must be
236 * updated to represent the remote machine's coordinate system before calling this
237 * function. This should be done via the transformToRemoteCoor dinates() method.
238 */
239 private void injectTouchEventData(MotionEvent event) {
240 int action = event.getActionMasked();
241 TouchEventData.EventType touchEventType = TouchEventData.EventType.fromM askedAction(action);
242 List<TouchEventData> touchEventList = new ArrayList<TouchEventData>();
243
244 if (action == MotionEvent.ACTION_MOVE) {
245 // In order to process all of the events associated with an ACTION_M OVE event, we need
246 // to walk the list of historical events in order and add each event to our list, then
247 // retrieve the current move event data.
248 int pointerCount = event.getPointerCount();
249 int historySize = event.getHistorySize();
250 for (int h = 0; h < historySize; ++h) {
251 for (int p = 0; p < pointerCount; ++p) {
252 touchEventList.add(new TouchEventData(event.getPointerId(p),
253 event.getHistoricalX(p, h), event.getHistoricalY(p, h),
254 event.getHistoricalSize(p, h), event.getHistoricalSi ze(p, h),
255 event.getHistoricalOrientation(p, h),
256 event.getHistoricalPressure(p, h)));
257 }
258 }
259
260 for (int p = 0; p < pointerCount; p++) {
261 touchEventList.add(new TouchEventData(event.getPointerId(p), eve nt.getX(p),
262 event.getY(p), event.getSize(p), event.getSize(p), event .getOrientation(p),
263 event.getPressure(p)));
264 }
265 } else {
266 // For all other events, we only want to grab the current/active poi nter. The event
267 // contains a list of every active pointer but passing all of of the se to the host can
268 // cause confusion on the remote OS side and result in broken touch gestures.
269 int activePointerIndex = event.getActionIndex();
270 touchEventList.add(new TouchEventData(event.getPointerId(activePoint erIndex),
271 event.getX(activePointerIndex), event.getY(activePointerInde x),
272 event.getSize(activePointerIndex), event.getSize(activePoint erIndex),
273 event.getOrientation(activePointerIndex),
274 event.getPressure(activePointerIndex)));
275 }
276
277 if (!touchEventList.isEmpty()) {
278 TouchEventData[] touchEventArray = new TouchEventData[touchEventList .size()];
279 mRemoteInputInjector.injectTouchEvent(
280 touchEventType, touchEventList.toArray(touchEventArray));
281 }
282 }
283
284 private void injectQueuedEvents() { 191 private void injectQueuedEvents() {
285 while (!mQueuedEvents.isEmpty()) { 192 while (!mQueuedEvents.isEmpty()) {
286 MotionEvent event = mQueuedEvents.remove(); 193 MotionEvent event = mQueuedEvents.remove();
287 injectTouchEventData(event); 194 mInjector.sendTouchEvent(event);
288 event.recycle(); 195 event.recycle();
289 } 196 }
290 } 197 }
291 198
292 private void clearQueuedEvents() { 199 private void clearQueuedEvents() {
293 while (!mQueuedEvents.isEmpty()) { 200 while (!mQueuedEvents.isEmpty()) {
294 mQueuedEvents.remove().recycle(); 201 mQueuedEvents.remove().recycle();
295 } 202 }
296 } 203 }
297 204
(...skipping 12 matching lines...) Expand all
310 217
311 return event; 218 return event;
312 } 219 }
313 220
314 private void resetStateData() { 221 private void resetStateData() {
315 clearQueuedEvents(); 222 clearQueuedEvents();
316 mInRemoteGesture = false; 223 mInRemoteGesture = false;
317 mIgnoreTouchEvents = false; 224 mIgnoreTouchEvents = false;
318 } 225 }
319 } 226 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698