diff options
author | lambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-01 11:52:58 +0000 |
---|---|---|
committer | lambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-01 11:52:58 +0000 |
commit | 99ab575fea9f14dab8bf92a7a402be8a79a6e87a (patch) | |
tree | 4cb29ae5d604f88725c0e50ccbd98576bb62e3e9 /remoting | |
parent | b796c34f3ff3ff161acb7e36665225f6807ad4c1 (diff) | |
download | chromium_src-99ab575fea9f14dab8bf92a7a402be8a79a6e87a.zip chromium_src-99ab575fea9f14dab8bf92a7a402be8a79a6e87a.tar.gz chromium_src-99ab575fea9f14dab8bf92a7a402be8a79a6e87a.tar.bz2 |
[Android Chromoting] Gesture detector for multiple-finger tap events.
This adds a gesture-detector for recognizing taps with multiple fingers,
which simplifies the TrackingInputHandler implementation and makes
tap-detection more robust.
BUG=270348
Review URL: https://codereview.chromium.org/51463004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@232333 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/android/java/src/org/chromium/chromoting/TapGestureDetector.java | 151 | ||||
-rw-r--r-- | remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java | 89 |
2 files changed, 178 insertions, 62 deletions
diff --git a/remoting/android/java/src/org/chromium/chromoting/TapGestureDetector.java b/remoting/android/java/src/org/chromium/chromoting/TapGestureDetector.java new file mode 100644 index 0000000..c0c2843 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/TapGestureDetector.java @@ -0,0 +1,151 @@ +// 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.graphics.PointF; +import android.util.Log; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +import java.util.HashMap; + +/** + * This class detects multi-finger tap events. This is provided since the stock Android + * gesture-detectors only detect taps made with one finger. + */ +public class TapGestureDetector { + /** The listener for receiving notifications of tap gestures. */ + public interface OnTapListener { + /** + * Notified when a tap event occurs. + * + * @param pointerCount The number of fingers that were tapped. + * @return True if the event is consumed. + */ + boolean onTap(int pointerCount); + } + + /** The listener to which notifications are sent. */ + private OnTapListener mListener; + + /** The maximum number of fingers seen in the gesture. */ + private int mPointerCount = 0; + + /** + * Stores the location of each down MotionEvent (by pointer ID), for detecting motion of any + * pointer beyond the TouchSlop region. + */ + private HashMap<Integer, PointF> mInitialPositions = new HashMap<Integer, PointF>(); + + /** + * Threshold squared-distance, in pixels, to use for motion-detection. If a finger moves less + * than this distance, the gesture is still eligible to be a tap event. + */ + private int mTouchSlopSquare; + + /** Set to true whenever motion is detected in the gesture. */ + private boolean mMotionOccurred = false; + + public TapGestureDetector(Context context, OnTapListener listener) { + mListener = listener; + ViewConfiguration config = ViewConfiguration.get(context); + int touchSlop = config.getScaledTouchSlop(); + mTouchSlopSquare = touchSlop * touchSlop; + } + + /** Analyzes the touch event to determine whether to notify the listener. */ + public boolean onTouchEvent(MotionEvent event) { + boolean handled = false; + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + trackDownEvent(event); + mPointerCount = Math.max(mPointerCount, event.getPointerCount()); + break; + + case MotionEvent.ACTION_MOVE: + if (!mMotionOccurred) { + mMotionOccurred = trackMoveEvent(event); + } + break; + + case MotionEvent.ACTION_UP: + if (!mMotionOccurred) { + handled = mListener.onTap(mPointerCount); + } + reset(); + break; + + case MotionEvent.ACTION_POINTER_UP: + trackUpEvent(event); + break; + + case MotionEvent.ACTION_CANCEL: + reset(); + break; + + default: + break; + } + return handled; + } + + /** Stores the location of the ACTION_DOWN or ACTION_POINTER_DOWN event. */ + private void trackDownEvent(MotionEvent event) { + int pointerIndex = 0; + if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { + pointerIndex = event.getActionIndex(); + } + int pointerId = event.getPointerId(pointerIndex); + mInitialPositions.put(pointerId, + new PointF(event.getX(pointerIndex), event.getY(pointerIndex))); + } + + /** Removes the ACTION_UP or ACTION_POINTER_UP event from the stored list. */ + private void trackUpEvent(MotionEvent event) { + int pointerIndex = 0; + if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) { + pointerIndex = event.getActionIndex(); + } + int pointerId = event.getPointerId(pointerIndex); + mInitialPositions.remove(pointerId); + } + + /** + * Processes an ACTION_MOVE event and returns whether a pointer moved beyond the TouchSlop + * threshold. + * + * @return True if motion was detected. + */ + private boolean trackMoveEvent(MotionEvent event) { + int pointerCount = event.getPointerCount(); + for (int i = 0; i < pointerCount; i++) { + int pointerId = event.getPointerId(i); + float currentX = event.getX(i); + float currentY = event.getY(i); + PointF downPoint = mInitialPositions.get(pointerId); + if (downPoint == null) { + // There was no corresponding DOWN event, so add it. This is an inconsistency + // which shouldn't normally occur. + mInitialPositions.put(pointerId, new PointF(currentX, currentY)); + continue; + } + float deltaX = currentX - downPoint.x; + float deltaY = currentY - downPoint.y; + if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) { + return true; + } + } + return false; + } + + /** Cleans up any stored data for the gesture. */ + private void reset() { + mPointerCount = 0; + mInitialPositions.clear(); + mMotionOccurred = false; + } +} diff --git a/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java b/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java index e7500d8..537f5aa 100644 --- a/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java +++ b/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java @@ -32,6 +32,7 @@ public class TrackingInputHandler implements TouchInputHandler { private GestureDetector mScroller; private ScaleGestureDetector mZoomer; + private TapGestureDetector mTapDetector; /** * The current cursor position is stored here as floats, so that the desktop image can be @@ -39,16 +40,6 @@ public class TrackingInputHandler implements TouchInputHandler { */ private PointF mCursorPosition; - private int mMouseButton; - - /** - * Distinguish between finger tap and swipe. One-finger down then up should inject a - * left-click event. But if the finger is dragged before being released, this should move - * the cursor without injecting any button event. This flag is set when a motion event is - * detected. - */ - private boolean mFingerMoved = false; - public TrackingInputHandler(DesktopViewInterface viewer, Context context, RenderData renderData) { mViewer = viewer; @@ -64,11 +55,9 @@ public class TrackingInputHandler implements TouchInputHandler { mScroller.setIsLongpressEnabled(false); mZoomer = new ScaleGestureDetector(context, listener); + mTapDetector = new TapGestureDetector(context, listener); mCursorPosition = new PointF(); - - mMouseButton = BUTTON_UNDEFINED; - mFingerMoved = false; } /** @@ -190,52 +179,7 @@ public class TrackingInputHandler implements TouchInputHandler { // that they generate correct notifications. boolean handled = mScroller.onTouchEvent(event); handled = mZoomer.onTouchEvent(event) || handled; - - int pointerCount = event.getPointerCount(); - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - switch (pointerCount) { - case 1: - mFingerMoved = false; - mMouseButton = BUTTON_LEFT; - break; - case 2: - mMouseButton = BUTTON_RIGHT; - break; - case 3: - mMouseButton = BUTTON_UNDEFINED; - // TODO(lambroslambrou): Add 3-finger-tap for middle-click, and use 3-finger - // swipe to show the action-bar or keyboard. - break; - default: - break; - } - break; - case MotionEvent.ACTION_UP: - if (mFingerMoved) { - // Don't inject anything. - mFingerMoved = false; - } else { - // The user pressed and released without moving. Inject a click event for a - // mouse button according to how many fingers were used. - injectButtonEvent(mMouseButton, true); - injectButtonEvent(mMouseButton, false); - handled = true; - } - break; - case MotionEvent.ACTION_POINTER_UP: - // |pointerCount| is the number of fingers that were on the screen prior to the UP - // event. - if (pointerCount == 3) { - mViewer.showActionBar(); - handled = true; - } - break; - default: - break; - } + handled = mTapDetector.onTouchEvent(event) || handled; return handled; } @@ -256,7 +200,8 @@ public class TrackingInputHandler implements TouchInputHandler { /** Responds to touch events filtered by the gesture detectors. */ private class GestureListener extends GestureDetector.SimpleOnGestureListener - implements ScaleGestureDetector.OnScaleGestureListener { + implements ScaleGestureDetector.OnScaleGestureListener, + TapGestureDetector.OnTapListener { /** * Called when the user drags one or more fingers across the touchscreen. */ @@ -265,7 +210,6 @@ public class TrackingInputHandler implements TouchInputHandler { if (e2.getPointerCount() != 1) { return false; } - mFingerMoved = true; float[] delta = {distanceX, distanceY}; synchronized (mRenderData) { @@ -284,7 +228,6 @@ public class TrackingInputHandler implements TouchInputHandler { if (Math.abs(detector.getScaleFactor() - 1) < MIN_ZOOM_DELTA) { return false; } - mFingerMoved = true; float scaleFactor = detector.getScaleFactor(); synchronized (mRenderData) { @@ -315,5 +258,27 @@ public class TrackingInputHandler implements TouchInputHandler { public void onScaleEnd(ScaleGestureDetector detector) { onScale(detector); } + + /** Called when the user taps the screen with one or more fingers. */ + @Override + public boolean onTap(int pointerCount) { + switch (pointerCount) { + case 1: + injectButtonEvent(BUTTON_LEFT, true); + injectButtonEvent(BUTTON_LEFT, false); + return true; + case 2: + injectButtonEvent(BUTTON_RIGHT, true); + injectButtonEvent(BUTTON_RIGHT, false); + return true; + case 3: + // TODO(lambroslambrou): Add 3-finger-tap for middle-click, and use 3-finger + // swipe to show the action-bar or keyboard. + mViewer.showActionBar(); + return true; + default: + return false; + } + } } } |