summaryrefslogtreecommitdiffstats
path: root/remoting/android
diff options
context:
space:
mode:
authorlambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-27 23:20:52 +0000
committerlambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-27 23:20:52 +0000
commitcaaa849e2e0cc9caf9a2fe938e43515c35bfa679 (patch)
tree6b5beb54119aa71deddb962dd3983f50c5fe56ae /remoting/android
parentf4acd55ec469865ec005791e54d46171880e80a0 (diff)
downloadchromium_src-caaa849e2e0cc9caf9a2fe938e43515c35bfa679.zip
chromium_src-caaa849e2e0cc9caf9a2fe938e43515c35bfa679.tar.gz
chromium_src-caaa849e2e0cc9caf9a2fe938e43515c35bfa679.tar.bz2
Add 2-finger swipe gesture for mouse-wheel in Chromoting Android client.
Since a two-finger gesture is also used for scaling, this adds some code to disambiguate the two gestures. R=jamiewalch@chromium.org Review URL: https://codereview.chromium.org/89553002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237641 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/android')
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/SwipePinchDetector.java161
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java23
2 files changed, 181 insertions, 3 deletions
diff --git a/remoting/android/java/src/org/chromium/chromoting/SwipePinchDetector.java b/remoting/android/java/src/org/chromium/chromoting/SwipePinchDetector.java
new file mode 100644
index 0000000..8f3b17f5
--- /dev/null
+++ b/remoting/android/java/src/org/chromium/chromoting/SwipePinchDetector.java
@@ -0,0 +1,161 @@
+// Copyright 2013 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.content.Context;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * Helper class for disambiguating whether to treat a two-finger gesture as a swipe or a pinch.
+ * Initially, the status will be unknown, until the fingers have moved sufficiently far to
+ * determine the intent.
+ */
+public class SwipePinchDetector {
+ /** Current state of the gesture. */
+ private enum State {
+ UNKNOWN,
+ SWIPE,
+ PINCH
+ }
+ private State mState = State.UNKNOWN;
+
+ /** Initial coordinates of the two pointers in the current gesture. */
+ private float mFirstX0;
+ private float mFirstY0;
+ private float mFirstX1;
+ private float mFirstY1;
+
+ /**
+ * The initial coordinates above are valid when this flag is set. Used to determine whether a
+ * MotionEvent's pointer coordinates are the first ones of the gesture.
+ */
+ private boolean mInGesture = false;
+
+ /**
+ * Threshold squared-distance, in pixels, to use for motion-detection.
+ */
+ private int mTouchSlopSquare;
+
+ private void reset() {
+ mState = State.UNKNOWN;
+ mInGesture = false;
+ }
+
+ /** Construct a new detector, using the context to determine movement thresholds. */
+ public SwipePinchDetector(Context context) {
+ ViewConfiguration config = ViewConfiguration.get(context);
+ int touchSlop = config.getScaledTouchSlop();
+ mTouchSlopSquare = touchSlop * touchSlop;
+ }
+
+ /** Returns whether a swipe is in progress. */
+ public boolean isSwiping() {
+ return mState == State.SWIPE;
+ }
+
+ /** Returns whether a pinch is in progress. */
+ public boolean isPinching() {
+ return mState == State.PINCH;
+ }
+
+ /**
+ * Analyzes the touch event to determine whether the user is swiping or pinching. Only
+ * motion events with 2 pointers are considered here. Once the gesture is determined to be a
+ * swipe or a pinch, further 2-finger motion-events will be ignored. When a different event is
+ * passed in (motion event with != 2 pointers, or some other event type), this object will
+ * revert back to the original UNKNOWN state.
+ */
+ public void onTouchEvent(MotionEvent event) {
+ if (event.getPointerCount() != 2) {
+ reset();
+ return;
+ }
+
+ // Only MOVE or DOWN events are considered - all other events should finish any current
+ // gesture and reset the detector. In addition, a DOWN event should reset the detector,
+ // since it signals the start of the gesture. If the events are consistent, a DOWN event
+ // will occur at the start of the gesture, but this implementation tries to cope in case
+ // the first event is MOVE rather than DOWN.
+ int action = event.getActionMasked();
+ if (action != MotionEvent.ACTION_MOVE) {
+ reset();
+ if (action != MotionEvent.ACTION_POINTER_DOWN) {
+ return;
+ }
+ }
+
+ // If the gesture is known, there is no need for further processing - the state should
+ // remain the same until the gesture is complete, as tested above.
+ if (mState != State.UNKNOWN) {
+ return;
+ }
+
+ float currentX0 = event.getX(0);
+ float currentY0 = event.getY(0);
+ float currentX1 = event.getX(1);
+ float currentY1 = event.getY(1);
+ if (!mInGesture) {
+ // This is the first event of the gesture, so store the pointer coordinates.
+ mFirstX0 = currentX0;
+ mFirstY0 = currentY0;
+ mFirstX1 = currentX1;
+ mFirstY1 = currentY1;
+ mInGesture = true;
+ return;
+ }
+
+ float deltaX0 = currentX0 - mFirstX0;
+ float deltaY0 = currentY0 - mFirstY0;
+ float deltaX1 = currentX1 - mFirstX1;
+ float deltaY1 = currentY1 - mFirstY1;
+
+ float squaredDistance0 = deltaX0 * deltaX0 + deltaY0 * deltaY0;
+ float squaredDistance1 = deltaX1 * deltaX1 + deltaY1 * deltaY1;
+
+
+ // If both fingers have moved beyond the touch-slop, it is safe to recognize the gesture.
+ // However, one finger might be held stationary whilst the other finger is moved a long
+ // distance. In this case, it is preferable to trigger a PINCH. This should be detected
+ // soon enough to avoid triggering a sudden large change in the zoom level, but not so
+ // soon that SWIPE never gets triggered.
+
+ // Threshold level for triggering the PINCH gesture if one finger is stationary. This
+ // cannot be equal to the touch-slop, because in that case, SWIPE would rarely be detected.
+ // One finger would usually leave the touch-slop radius slightly before the other finger,
+ // triggering a PINCH as described above. A larger radius gives an opportunity for
+ // SWIPE to be detected. Doubling the radius is an arbitrary choice that works well.
+ int pinchThresholdSquare = 4 * mTouchSlopSquare;
+
+ boolean finger0Moved = squaredDistance0 > mTouchSlopSquare;
+ boolean finger1Moved = squaredDistance1 > mTouchSlopSquare;
+
+ if (!finger0Moved && !finger1Moved) {
+ return;
+ }
+
+ if (finger0Moved && !finger1Moved) {
+ if (squaredDistance0 > pinchThresholdSquare) {
+ mState = State.PINCH;
+ }
+ return;
+ }
+
+ if (!finger0Moved && finger1Moved) {
+ if (squaredDistance1 > pinchThresholdSquare) {
+ mState = State.PINCH;
+ }
+ return;
+ }
+
+ // Both fingers have moved, so determine SWIPE/PINCH status. If the fingers have moved in
+ // the same direction, this is a SWIPE, otherwise it's a PINCH. This can be measured by
+ // taking the scalar product of the direction vectors. This product is positive if the
+ // vectors are pointing in the same direction, and negative if they're in opposite
+ // directions.
+ float scalarProduct = deltaX0 * deltaX1 + deltaY0 * deltaY1;
+ mState = (scalarProduct > 0) ? State.SWIPE : State.PINCH;
+ }
+}
diff --git a/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java b/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java
index f26b929..b1cd3e4 100644
--- a/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java
+++ b/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java
@@ -38,6 +38,9 @@ public class TrackingInputHandler implements TouchInputHandler {
/** Used to calculate the physics for flinging the cursor. */
private Scroller mFlingScroller;
+ /** Used to disambiguate a 2-finger gesture as a swipe or a pinch. */
+ private SwipePinchDetector mSwipePinchDetector;
+
/**
* The current cursor position is stored here as floats, so that the desktop image can be
* positioned with sub-pixel accuracy, to give a smoother panning animation at high zoom levels.
@@ -93,8 +96,8 @@ public class TrackingInputHandler implements TouchInputHandler {
mZoomer = new ScaleGestureDetector(context, listener);
mTapDetector = new TapGestureDetector(context, listener);
-
mFlingScroller = new Scroller(context);
+ mSwipePinchDetector = new SwipePinchDetector(context);
mCursorPosition = new PointF();
@@ -251,6 +254,7 @@ public class TrackingInputHandler implements TouchInputHandler {
boolean handled = mScroller.onTouchEvent(event);
handled |= mZoomer.onTouchEvent(event);
handled |= mTapDetector.onTouchEvent(event);
+ mSwipePinchDetector.onTouchEvent(event);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
@@ -318,7 +322,8 @@ public class TrackingInputHandler implements TouchInputHandler {
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- if (e2.getPointerCount() == 3 && !mSwipeCompleted) {
+ int pointerCount = e2.getPointerCount();
+ if (pointerCount == 3 && !mSwipeCompleted) {
// Note that distance values are reversed. For example, dragging a finger in the
// direction of increasing Y coordinate (downwards) results in distanceY being
// negative.
@@ -326,7 +331,15 @@ public class TrackingInputHandler implements TouchInputHandler {
return onSwipe();
}
- if (e2.getPointerCount() != 1 || mSuppressCursorMovement) {
+ if (pointerCount == 2 && mSwipePinchDetector.isSwiping()) {
+ mViewer.injectMouseWheelDeltaEvent(-(int)distanceX, -(int)distanceY);
+
+ // Prevent the cursor being moved or flung by the gesture.
+ mSuppressCursorMovement = true;
+ return true;
+ }
+
+ if (pointerCount != 1 || mSuppressCursorMovement) {
return false;
}
@@ -373,6 +386,10 @@ public class TrackingInputHandler implements TouchInputHandler {
/** Called when the user is in the process of pinch-zooming. */
@Override
public boolean onScale(ScaleGestureDetector detector) {
+ if (!mSwipePinchDetector.isPinching()) {
+ return false;
+ }
+
if (Math.abs(detector.getScaleFactor() - 1) < MIN_ZOOM_DELTA) {
return false;
}