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