Chromium Code Reviews| 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.content.Context; | |
| 8 import android.graphics.Rect; | |
| 9 import android.view.GestureDetector; | |
| 10 import android.view.MotionEvent; | |
| 11 import android.view.ScaleGestureDetector; | |
| 12 import android.view.ViewConfiguration; | |
| 13 | |
| 14 import org.chromium.base.VisibleForTesting; | |
| 15 import org.chromium.chromoting.InputState.DetectedAction; | |
| 16 import org.chromium.chromoting.InputState.StartAction; | |
| 17 | |
| 18 /** | |
| 19 * A combination of existing Android and chromium motion and touch detectors, an d provide a set of | |
| 20 * {@link Event} when each kind of touch behavior has been detected. | |
| 21 */ | |
| 22 public final class InputMonitor | |
| 23 implements GestureDetector.OnGestureListener, | |
| 24 ScaleGestureDetector.OnScaleGestureListener, | |
| 25 TapGestureDetector.OnTapListener { | |
| 26 /** Tap with one or more fingers. */ | |
| 27 private final Event.Raisable<TapEventParameter> mOnTap; | |
| 28 | |
| 29 /** Long press and hold with one or more fingers. */ | |
| 30 private final Event.Raisable<TapEventParameter> mOnPressAndHold; | |
| 31 | |
| 32 /** Any motion event received. */ | |
| 33 private final Event.Raisable<MotionEvent> mOnTouchEvent; | |
| 34 | |
| 35 /** Scroll with two fingers. */ | |
| 36 private final Event.Raisable<TwoPointsEventParameter> mOnScroll; | |
| 37 | |
| 38 /** Fling with two fingers. */ | |
| 39 private final Event.Raisable<TwoPointsEventParameter> mOnScrollFling; | |
| 40 | |
| 41 /** Fling with one finger. */ | |
| 42 private final Event.Raisable<TwoPointsEventParameter> mOnFling; | |
| 43 | |
| 44 /** Scale with two fingers. */ | |
| 45 private final Event.Raisable<ScaleEventParameter> mOnScale; | |
| 46 | |
| 47 /** Swipe with three or more fingers. */ | |
| 48 private final Event.Raisable<TwoPointsEventParameter> mOnSwipe; | |
| 49 | |
| 50 /** Move with one finger. */ | |
| 51 private final Event.Raisable<TwoPointsEventParameter> mOnMove; | |
| 52 | |
| 53 private final InputState.Settable mInputState; | |
| 54 private final int mEdgeSlopInPx; | |
| 55 private final float mSwipeThreshold; | |
| 56 private final GestureDetector mScroller; | |
| 57 private final ScaleGestureDetector mZoomer; | |
| 58 private final TapGestureDetector mTapDetector; | |
| 59 private final SwipePinchDetector mSwipePinchDetector; | |
| 60 | |
| 61 // Controls whether InputMonitor treats a scrolling with four or more finger s as a swipe. | |
| 62 // TouchInputHandler treats a gesture with three fingers as a swipe. To make sure we can easily | |
|
Lambros
2016/07/14 18:58:07
I don't see the need for this flag and I don't thi
Hzj_jie
2016/07/14 23:18:06
Personally I think swiping with three or four fing
| |
| 63 // test the differences between TouchInputHandler and InputMonitor, we can s et this variable to | |
|
Lambros
2016/07/14 18:58:07
Also, better to avoid flags that make code behave
Hzj_jie
2016/07/14 23:18:06
Done.
| |
| 64 // false to force InputMonitor to behave consistently as TouchInputHandler. | |
| 65 private final boolean mSwipeWithMoreFingers; | |
| 66 | |
| 67 private Rect mPanGestureBounds; | |
| 68 | |
| 69 @VisibleForTesting | |
| 70 InputMonitor(DesktopViewInterface view, Context context, boolean swipeWithMo reFingers) { | |
| 71 mOnTap = new Event.Raisable<>(); | |
| 72 mOnPressAndHold = new Event.Raisable<>(); | |
| 73 mOnTouchEvent = new Event.Raisable<>(); | |
| 74 mOnScroll = new Event.Raisable<>(); | |
| 75 mOnScrollFling = new Event.Raisable<>(); | |
| 76 mOnFling = new Event.Raisable<>(); | |
| 77 mOnScale = new Event.Raisable<>(); | |
| 78 mOnSwipe = new Event.Raisable<>(); | |
| 79 mOnMove = new Event.Raisable<>(); | |
| 80 mInputState = new InputState.Settable(); | |
| 81 mEdgeSlopInPx = ViewConfiguration.get(context).getScaledEdgeSlop(); | |
| 82 mSwipeThreshold = 40 * context.getResources().getDisplayMetrics().densit y; | |
| 83 mScroller = new GestureDetector(context, this, null, false); | |
| 84 mScroller.setIsLongpressEnabled(false); | |
| 85 mZoomer = new ScaleGestureDetector(context, this); | |
| 86 mTapDetector = new TapGestureDetector(context, this); | |
| 87 mSwipePinchDetector = new SwipePinchDetector(context); | |
| 88 view.onClientSizeChanged().add( | |
| 89 new Event.ParameterRunnable<SizeChangedEventParameter>() { | |
| 90 @Override | |
| 91 public void run(SizeChangedEventParameter param) { | |
| 92 handleClientSizeChanged(param); | |
| 93 } | |
| 94 }); | |
| 95 // Currently we support only touch events. | |
| 96 view.onTouch().add( | |
| 97 new Event.ParameterRunnable<TouchEventParameter>() { | |
| 98 @Override | |
| 99 public void run(TouchEventParameter param) { | |
| 100 handleTouch(param); | |
| 101 } | |
| 102 }); | |
| 103 mSwipeWithMoreFingers = swipeWithMoreFingers; | |
| 104 } | |
| 105 | |
| 106 public InputMonitor(DesktopViewInterface view, Context context) { | |
| 107 this(view, context, true); | |
| 108 } | |
| 109 | |
| 110 // -------------- Getters -------------------------------------------------- ----------- | |
| 111 public Event<TapEventParameter> onTap() { | |
| 112 return mOnTap; | |
| 113 } | |
| 114 | |
| 115 public Event<TapEventParameter> onPressAndHold() { | |
| 116 return mOnPressAndHold; | |
| 117 } | |
| 118 | |
| 119 public Event<MotionEvent> onTouchEvent() { | |
| 120 return mOnTouchEvent; | |
| 121 } | |
| 122 | |
| 123 public Event<TwoPointsEventParameter> onScroll() { | |
| 124 return mOnScroll; | |
| 125 } | |
| 126 | |
| 127 public Event<TwoPointsEventParameter> onScrollFling() { | |
| 128 return mOnScrollFling; | |
| 129 } | |
| 130 | |
| 131 public Event<TwoPointsEventParameter> onFling() { | |
| 132 return mOnFling; | |
| 133 } | |
| 134 | |
| 135 public Event<ScaleEventParameter> onScale() { | |
| 136 return mOnScale; | |
| 137 } | |
| 138 | |
| 139 public Event<TwoPointsEventParameter> onSwipe() { | |
| 140 return mOnSwipe; | |
| 141 } | |
| 142 | |
| 143 public Event<TwoPointsEventParameter> onMove() { | |
| 144 return mOnMove; | |
| 145 } | |
| 146 | |
| 147 public InputState inputState() { | |
| 148 return mInputState; | |
| 149 } | |
| 150 | |
| 151 // -------------- Implementations of GestureDetector.OnGestureListener ----- ----------- | |
| 152 | |
| 153 /** Called whenever a gesture starts. Always accepts the gesture so it isn't ignored. */ | |
| 154 @Override | |
| 155 public boolean onDown(MotionEvent e) { | |
| 156 return true; | |
| 157 } | |
| 158 | |
| 159 /** Called when a fling gesture is recognized. */ | |
| 160 @Override | |
| 161 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, floa t velocityY) { | |
| 162 if (mInputState.suppressFling()) { | |
| 163 return false; | |
| 164 } | |
| 165 | |
| 166 if (mInputState.scrollFling()) { | |
| 167 mInputState.setDetectedAction(DetectedAction.AFTER_SCROLL_FLING); | |
| 168 mOnScrollFling.raise(new TwoPointsEventParameter(e1, e2, velocityX, velocityY)); | |
| 169 return true; | |
| 170 } | |
| 171 | |
| 172 if (mInputState.suppressCursorMovement()) { | |
| 173 return false; | |
| 174 } | |
| 175 | |
| 176 mInputState.setDetectedAction(DetectedAction.FLING); | |
| 177 mOnFling.raise(new TwoPointsEventParameter(e1, e2, velocityX, velocityY) ); | |
| 178 return true; | |
| 179 } | |
| 180 | |
| 181 /** Called when a long-press is triggered for one or more fingers. */ | |
| 182 @Override | |
| 183 public void onLongPress(MotionEvent e) {} | |
| 184 | |
| 185 /** Called when the user drags one or more fingers across the touchscreen. * / | |
| 186 @Override | |
| 187 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, flo at distanceY) { | |
| 188 if (!isInPanGestureBounds(e1)) { | |
| 189 // The gesture of scrolling from edge to the center should be handle d by Android OS. | |
| 190 mInputState.setDetectedAction(InputState.DetectedAction.SCROLL_EDGE) ; | |
| 191 return false; | |
| 192 } | |
| 193 | |
| 194 if (((mSwipeWithMoreFingers && e2.getPointerCount() >= 3) || e2.getPoint erCount() == 3) | |
| 195 && !mInputState.swipeCompleted()) { | |
| 196 if (distanceY > mSwipeThreshold || distanceY < -mSwipeThreshold) { | |
| 197 mInputState.setDetectedAction(DetectedAction.SWIPE); | |
| 198 mOnSwipe.raise(new TwoPointsEventParameter(e1, e2, distanceX, di stanceY)); | |
| 199 return true; | |
| 200 } | |
| 201 return false; | |
| 202 } | |
| 203 | |
| 204 if (e2.getPointerCount() == 2 && mSwipePinchDetector.isSwiping()) { | |
| 205 mInputState.setDetectedAction(DetectedAction.SCROLL); | |
| 206 mOnScroll.raise(new TwoPointsEventParameter(e1, e2, distanceX, dista nceY)); | |
| 207 return true; | |
| 208 } | |
| 209 | |
| 210 if (e2.getPointerCount() != 1 || mInputState.suppressCursorMovement()) { | |
| 211 return false; | |
| 212 } | |
| 213 | |
| 214 mInputState.setDetectedAction(DetectedAction.MOVE); | |
| 215 mOnMove.raise(new TwoPointsEventParameter(e1, e2, distanceX, distanceY)) ; | |
| 216 return true; | |
| 217 } | |
| 218 | |
| 219 /** Called by {@link GestureDetector}, does nothing. */ | |
| 220 @Override | |
| 221 public void onShowPress(MotionEvent e) {} | |
| 222 | |
| 223 /** Called by {@link GestureDetector}, returns false to continue following g esture detection. */ | |
| 224 @Override | |
| 225 public boolean onSingleTapUp(MotionEvent e) { | |
| 226 return false; | |
| 227 } | |
| 228 | |
| 229 // --------- Implementations of ScaleGestureDetector.OnScaleGestureListener ----------- | |
| 230 /** Called when the user is in the process of pinch-zooming. */ | |
| 231 @Override | |
| 232 public boolean onScale(ScaleGestureDetector detector) { | |
| 233 if (!mSwipePinchDetector.isPinching()) { | |
| 234 return false; | |
| 235 } | |
| 236 | |
| 237 mInputState.setDetectedAction(DetectedAction.SCALE); | |
| 238 mOnScale.raise(new ScaleEventParameter(detector.getScaleFactor(), | |
| 239 detector.getFocusX(), | |
| 240 detector.getFocusY())); | |
| 241 return true; | |
| 242 } | |
| 243 | |
| 244 /** | |
| 245 * Called when the user starts to zoom. Always accepts the zoom so that | |
| 246 * onScale() can decide whether to respond to it. | |
| 247 */ | |
| 248 @Override | |
| 249 public boolean onScaleBegin(ScaleGestureDetector detector) { | |
| 250 return true; | |
| 251 } | |
| 252 | |
| 253 /** Called when the user is done zooming. Defers to onScale()'s judgement. * / | |
| 254 @Override | |
| 255 public void onScaleEnd(ScaleGestureDetector detector) { | |
| 256 onScale(detector); | |
| 257 } | |
| 258 | |
| 259 // -------------- Implementations of TapGestureDetector.OnTapListener ------ ---------- | |
| 260 /** Called when the user taps the screen with one or more fingers. */ | |
| 261 @Override | |
| 262 public boolean onTap(int pointerCount, float x, float y) { | |
| 263 TapEventParameter para = new TapEventParameter(pointerCount, x, y); | |
| 264 mOnTap.raise(para); | |
| 265 return para.handled; | |
| 266 } | |
| 267 | |
| 268 /** Called when a long-press is triggered for one or more fingers. */ | |
| 269 @Override | |
| 270 public void onLongPress(int pointerCount, float x, float y) { | |
| 271 TapEventParameter para = new TapEventParameter(pointerCount, x, y); | |
| 272 mOnPressAndHold.raise(para); | |
| 273 if (para.handled) { | |
| 274 mInputState.setStartAction(StartAction.LONG_PRESS); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 /** | |
| 279 * Returns a boolean value to indicate whether the MotionEvent is in the ran ge of | |
| 280 * {@link mPanGestureBounds} | |
| 281 */ | |
| 282 private boolean isInPanGestureBounds(MotionEvent e) { | |
| 283 Preconditions.notNull(e); | |
| 284 return mPanGestureBounds == null | |
| 285 || mPanGestureBounds.contains((int) e.getX(), (int) e.getY()); | |
| 286 } | |
| 287 | |
| 288 // ---------------------------- Event handlers ----------------------------- ---------- | |
| 289 private void handleClientSizeChanged(SizeChangedEventParameter parameter) { | |
| 290 mPanGestureBounds = new Rect(mEdgeSlopInPx, | |
| 291 mEdgeSlopInPx, | |
| 292 parameter.width - mEdgeSlopInPx, | |
| 293 parameter.height - mEdgeSlopInPx); | |
| 294 } | |
| 295 | |
| 296 private void handleTouch(TouchEventParameter parameter) { | |
| 297 mOnTouchEvent.raise(parameter.event); | |
| 298 | |
| 299 boolean handled = mScroller.onTouchEvent(parameter.event); | |
| 300 handled |= mZoomer.onTouchEvent(parameter.event); | |
| 301 handled |= mTapDetector.onTouchEvent(parameter.event); | |
| 302 mSwipePinchDetector.onTouchEvent(parameter.event); | |
| 303 | |
| 304 parameter.handled = handled; | |
| 305 } | |
| 306 } | |
| OLD | NEW |